diff options
Diffstat (limited to 'src/backend/catalog/pg_constraint.c')
-rw-r--r-- | src/backend/catalog/pg_constraint.c | 117 |
1 files changed, 116 insertions, 1 deletions
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index 45a99af774e..aaf3537d3f5 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -19,6 +19,7 @@ #include "access/htup_details.h" #include "access/sysattr.h" #include "access/table.h" +#include "access/xact.h" #include "catalog/catalog.h" #include "catalog/dependency.h" #include "catalog/heap.h" @@ -933,6 +934,8 @@ RemoveConstraintById(Oid conId) Relation conDesc; HeapTuple tup; Form_pg_constraint con; + bool dropping_pk = false; + List *unconstrained_cols = NIL; conDesc = table_open(ConstraintRelationId, RowExclusiveLock); @@ -957,7 +960,9 @@ RemoveConstraintById(Oid conId) /* * We need to update the relchecks count if it is a check constraint * being dropped. This update will force backends to rebuild relcache - * entries when we commit. + * entries when we commit. For not-null and primary key constraints, + * obtain the list of columns affected, so that we can reset their + * attnotnull flags below. */ if (con->contype == CONSTRAINT_CHECK) { @@ -984,6 +989,36 @@ RemoveConstraintById(Oid conId) table_close(pgrel, RowExclusiveLock); } + else if (con->contype == CONSTRAINT_NOTNULL) + { + unconstrained_cols = list_make1_int(extractNotNullColumn(tup)); + } + else if (con->contype == CONSTRAINT_PRIMARY) + { + Datum adatum; + ArrayType *arr; + int numkeys; + bool isNull; + int16 *attnums; + + dropping_pk = true; + + adatum = heap_getattr(tup, Anum_pg_constraint_conkey, + RelationGetDescr(conDesc), &isNull); + if (isNull) + elog(ERROR, "null conkey for constraint %u", con->oid); + arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */ + numkeys = ARR_DIMS(arr)[0]; + if (ARR_NDIM(arr) != 1 || + numkeys < 0 || + ARR_HASNULL(arr) || + ARR_ELEMTYPE(arr) != INT2OID) + elog(ERROR, "conkey is not a 1-D smallint array"); + attnums = (int16 *) ARR_DATA_PTR(arr); + + for (int i = 0; i < numkeys; i++) + unconstrained_cols = lappend_int(unconstrained_cols, attnums[i]); + } /* Keep lock on constraint's rel until end of xact */ table_close(rel, NoLock); @@ -1003,6 +1038,86 @@ RemoveConstraintById(Oid conId) /* Fry the constraint itself */ CatalogTupleDelete(conDesc, &tup->t_self); + /* + * If this was a NOT NULL or the primary key, the constrained columns must + * have had pg_attribute.attnotnull set. See if we need to reset it, and + * do so. + */ + if (unconstrained_cols != NIL) + { + Relation tablerel; + Relation attrel; + Bitmapset *pkcols; + ListCell *lc; + + /* Make the above deletion visible */ + CommandCounterIncrement(); + + tablerel = table_open(con->conrelid, NoLock); /* already have lock */ + attrel = table_open(AttributeRelationId, RowExclusiveLock); + + /* + * We want to test columns for their presence in the primary key, but + * only if we're not dropping it. + */ + pkcols = dropping_pk ? NULL : + RelationGetIndexAttrBitmap(tablerel, + INDEX_ATTR_BITMAP_PRIMARY_KEY); + + foreach(lc, unconstrained_cols) + { + AttrNumber attnum = lfirst_int(lc); + HeapTuple atttup; + HeapTuple contup; + Bitmapset *ircols; + Form_pg_attribute attForm; + + /* + * Obtain pg_attribute tuple and verify conditions on it. We use + * a copy we can scribble on. + */ + atttup = SearchSysCacheCopyAttNum(con->conrelid, attnum); + if (!HeapTupleIsValid(atttup)) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + attnum, con->conrelid); + attForm = (Form_pg_attribute) GETSTRUCT(atttup); + + /* + * Since the above deletion has been made visible, we can now + * search for any remaining constraints setting this column as + * not-nullable; if we find any, no need to reset attnotnull. + */ + if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, + pkcols)) + continue; + contup = findNotNullConstraintAttnum(con->conrelid, attnum); + if (contup) + continue; + + /* + * Also no reset if the column is in the replica identity or it's + * a generated column + */ + if (attForm->attidentity != '\0') + continue; + ircols = RelationGetIndexAttrBitmap(tablerel, + INDEX_ATTR_BITMAP_IDENTITY_KEY); + if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, + ircols)) + continue; + + /* Reset attnotnull */ + if (attForm->attnotnull) + { + attForm->attnotnull = false; + CatalogTupleUpdate(attrel, &atttup->t_self, atttup); + } + } + + table_close(attrel, RowExclusiveLock); + table_close(tablerel, NoLock); + } + /* Clean up */ ReleaseSysCache(tup); table_close(conDesc, RowExclusiveLock); |