ListCell *lc;
ObjectAddress pgaddress;
+ /*
+ * ShareRowExclusiveLock is required because this command runs some
+ * graph-wide consistency checks that wouldn't work if more than one ALTER
+ * PROPERTY GRAPH could operate on the same graph at once.
+ */
pgrelid = RangeVarGetRelidExtended(stmt->pgname,
ShareRowExclusiveLock,
stmt->missing_ok ? RVR_MISSING_OK : 0,
{
Oid peoid;
Oid labeloid;
- Oid ellabeloid;
+ Oid ellabeloid = InvalidOid;
ObjectAddress obj;
+ Relation ellabelrel;
+ SysScanDesc ellabelscan;
+ ScanKeyData ellabelkey[1];
+ int nlabels;
+ HeapTuple tuple;
if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
get_rel_name(pgrelid), stmt->element_alias, stmt->drop_label),
parser_errposition(pstate, -1));
- ellabeloid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL,
- Anum_pg_propgraph_element_label_oid,
- ObjectIdGetDatum(peoid),
- ObjectIdGetDatum(labeloid));
+ /*
+ * Is the given label associated with the element? Is this the only
+ * label associated with the element? Scan the
+ * pg_propgraph_element_label table to find answers to these
+ * questions. Stop scanning when we know both answers.
+ */
+ ellabelrel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&ellabelkey[0],
+ Anum_pg_propgraph_element_label_pgelelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(peoid));
+ ellabelscan = systable_beginscan(ellabelrel, PropgraphElementLabelElementLabelIndexId,
+ true, NULL, 1, ellabelkey);
+ nlabels = 0;
+ while (HeapTupleIsValid(tuple = systable_getnext(ellabelscan)))
+ {
+ Form_pg_propgraph_element_label ellabelform = (Form_pg_propgraph_element_label) GETSTRUCT(tuple);
+
+ nlabels++;
+
+ if (ellabelform->pgellabelid == labeloid)
+ ellabeloid = ellabelform->oid;
+
+ if (nlabels > 1 && ellabeloid)
+ break;
+ }
+ systable_endscan(ellabelscan);
+ table_close(ellabelrel, AccessShareLock);
if (!ellabeloid)
ereport(ERROR,
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 associated with it.
+ */
+ if (nlabels == 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 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);
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.
+-- Restore the dropped label for further tests
+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
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
+-- Restore the dropped label for further tests
+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);