From 8e3373ec7682b881c5b12c0ebf6f68ba860ebb45 Mon Sep 17 00:00:00 2001
From: satyanarayana narlapuram <satyanarlapuram@gmail.com>
Date: Mon, 20 Apr 2026 18:07:40 +0000
Subject: [PATCH] Prevent dropping the last label from a property graph element

ALTER PROPERTY GRAPH ... DROP LABEL allowed removing the last label
from an element, leaving it with zero labels. This causes 
pg_get_propgraphdef() to crash.

Add a check in AlterPropGraph() that counts the element's labels
before deletion and errors out if only one remains.

Reported-by: Satyanarayana Narlapuram <satyanarlapuram@gmail.com>
---
 src/backend/commands/propgraphcmds.c          | 30 +++++++++++++++++++
 .../expected/create_property_graph.out        |  6 ++++
 .../regress/sql/create_property_graph.sql     |  4 +++
 3 files changed, 40 insertions(+)

diff --git a/src/backend/commands/propgraphcmds.c b/src/backend/commands/propgraphcmds.c
index 45d2ff1b..16ef611b 100644
--- a/src/backend/commands/propgraphcmds.c
+++ b/src/backend/commands/propgraphcmds.c
@@ -1522,6 +1522,36 @@ AlterPropGraph(ParseState *pstate, const AlterPropGraphStmt *stmt)
 						   get_rel_name(pgrelid), stmt->element_alias, stmt->drop_label),
 					parser_errposition(pstate, -1));
 
+		/*
+		 * Prevent dropping the last label from an element. Every element
+		 * must have at least one label.
+		 */
+		{
+			Relation	elrel;
+			SysScanDesc elscan;
+			ScanKeyData elkey[1];
+			int			nlabels = 0;
+
+			elrel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+			ScanKeyInit(&elkey[0],
+						Anum_pg_propgraph_element_label_pgelelid,
+						BTEqualStrategyNumber, F_OIDEQ,
+						ObjectIdGetDatum(peoid));
+			elscan = systable_beginscan(elrel, PropgraphElementLabelElementLabelIndexId,
+										true, NULL, 1, elkey);
+			while (HeapTupleIsValid(systable_getnext(elscan)))
+				nlabels++;
+			systable_endscan(elscan);
+			table_close(elrel, AccessShareLock);
+
+			if (nlabels <= 1)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+						 errmsg("cannot drop the last label from element \"%s\"",
+								stmt->element_alias),
+						 errhint("Every element must have at least one label.")));
+		}
+
 		ObjectAddressSet(obj, PropgraphElementLabelRelationId, ellabeloid);
 		performDeletion(&obj, stmt->drop_behavior, 0);
 
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596e..740f886c 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -57,6 +57,12 @@ ALTER PROPERTY GRAPH g3
 ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 DROP LABEL t3l3x;  -- error
 ERROR:  property graph "g3" element "t3" has no label "t3l3x"
 ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 DROP LABEL t3l3;
+-- Test that the last label on an element cannot be dropped
+ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 DROP LABEL t3l2;
+ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 DROP LABEL t3l1;  -- error: last label
+ERROR:  cannot drop the last label from element "t3"
+HINT:  Every element must have at least one label.
+ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 ADD LABEL t3l2 PROPERTIES ALL COLUMNS;
 ALTER PROPERTY GRAPH g3 DROP VERTEX TABLES (t2);  -- fail
 ERROR:  cannot drop vertex t2 of property graph g3 because other objects depend on it
 DETAIL:  edge e1 of property graph g3 depends on vertex t2 of property graph g3
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df..4cf77159 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -52,6 +52,10 @@ ALTER PROPERTY GRAPH g3
         ADD LABEL t3l3 PROPERTIES ALL COLUMNS;
 ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 DROP LABEL t3l3x;  -- error
 ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 DROP LABEL t3l3;
+-- Test that the last label on an element cannot be dropped
+ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 DROP LABEL t3l2;
+ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 DROP LABEL t3l1;  -- error: last label
+ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 ADD LABEL t3l2 PROPERTIES ALL COLUMNS;
 ALTER PROPERTY GRAPH g3 DROP VERTEX TABLES (t2);  -- fail
 ALTER PROPERTY GRAPH g3 DROP VERTEX TABLES (t2) CASCADE;
 ALTER PROPERTY GRAPH g3 DROP EDGE TABLES (e2);
-- 
2.43.0

