diff options
Diffstat (limited to 'src/backend/catalog/pg_constraint.c')
-rw-r--r-- | src/backend/catalog/pg_constraint.c | 441 |
1 files changed, 1 insertions, 440 deletions
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index 12a73d5a309..b10e458b449 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -19,10 +19,8 @@ #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" #include "catalog/indexing.h" #include "catalog/objectaccess.h" #include "catalog/pg_constraint.h" @@ -566,72 +564,6 @@ ChooseConstraintName(const char *name1, const char *name2, } /* - * Find and return a copy of the pg_constraint tuple that implements a - * validated not-null constraint for the given column of the given relation. - * - * XXX This would be easier if we had pg_attribute.notnullconstr with the OID - * of the constraint that implements the not-null constraint for that column. - * I'm not sure it's worth the catalog bloat and de-normalization, however. - */ -HeapTuple -findNotNullConstraintAttnum(Oid relid, AttrNumber attnum) -{ - Relation pg_constraint; - HeapTuple conTup, - retval = NULL; - SysScanDesc scan; - ScanKeyData key; - - pg_constraint = table_open(ConstraintRelationId, AccessShareLock); - ScanKeyInit(&key, - Anum_pg_constraint_conrelid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(relid)); - scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, - true, NULL, 1, &key); - - while (HeapTupleIsValid(conTup = systable_getnext(scan))) - { - Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(conTup); - AttrNumber conkey; - - /* - * We're looking for a NOTNULL constraint that's marked validated, - * with the column we're looking for as the sole element in conkey. - */ - if (con->contype != CONSTRAINT_NOTNULL) - continue; - if (!con->convalidated) - continue; - - conkey = extractNotNullColumn(conTup); - if (conkey != attnum) - continue; - - /* Found it */ - retval = heap_copytuple(conTup); - break; - } - - systable_endscan(scan); - table_close(pg_constraint, AccessShareLock); - - return retval; -} - -/* - * Find and return the pg_constraint tuple that implements a validated - * not-null constraint for the given column of the given relation. - */ -HeapTuple -findNotNullConstraint(Oid relid, const char *colname) -{ - AttrNumber attnum = get_attnum(relid, colname); - - return findNotNullConstraintAttnum(relid, attnum); -} - -/* * Find and return the pg_constraint tuple that implements a validated * not-null constraint for the given domain. */ @@ -676,263 +608,6 @@ findDomainNotNullConstraint(Oid typid) } /* - * Given a pg_constraint tuple for a not-null constraint, return the column - * number it is for. - */ -AttrNumber -extractNotNullColumn(HeapTuple constrTup) -{ - AttrNumber colnum; - Datum adatum; - ArrayType *arr; - - /* only tuples for not-null constraints should be given */ - Assert(((Form_pg_constraint) GETSTRUCT(constrTup))->contype == CONSTRAINT_NOTNULL); - - adatum = SysCacheGetAttrNotNull(CONSTROID, constrTup, - Anum_pg_constraint_conkey); - arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */ - if (ARR_NDIM(arr) != 1 || - ARR_HASNULL(arr) || - ARR_ELEMTYPE(arr) != INT2OID || - ARR_DIMS(arr)[0] != 1) - elog(ERROR, "conkey is not a 1-D smallint array"); - - memcpy(&colnum, ARR_DATA_PTR(arr), sizeof(AttrNumber)); - - if ((Pointer) arr != DatumGetPointer(adatum)) - pfree(arr); /* free de-toasted copy, if any */ - - return colnum; -} - -/* - * AdjustNotNullInheritance1 - * Adjust inheritance count for a single not-null constraint - * - * If no not-null constraint is found for the column, return 0. - * Caller can create one. - * If the constraint does exist and it's inheritable, adjust its - * inheritance count (and possibly islocal status) and return 1. - * No further action needs to be taken. - * If the constraint exists but is marked NO INHERIT, adjust it as above - * and reset connoinherit to false, and return -1. Caller is - * responsible for adding the same constraint to the children, if any. - */ -int -AdjustNotNullInheritance1(Oid relid, AttrNumber attnum, int count, - bool is_no_inherit, bool allow_noinherit_change) -{ - HeapTuple tup; - - Assert(count >= 0); - - tup = findNotNullConstraintAttnum(relid, attnum); - if (HeapTupleIsValid(tup)) - { - Relation pg_constraint; - Form_pg_constraint conform; - int retval = 1; - - pg_constraint = table_open(ConstraintRelationId, RowExclusiveLock); - conform = (Form_pg_constraint) GETSTRUCT(tup); - - /* - * If we're asked for a NO INHERIT constraint and this relation - * already has an inheritable one, throw an error. - */ - if (is_no_inherit && !conform->connoinherit) - ereport(ERROR, - errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"", - NameStr(conform->conname), get_rel_name(relid))); - - /* - * If the constraint already exists in this relation but it's marked - * NO INHERIT, we can just remove that flag (provided caller allows - * such a change), and instruct caller to recurse to add the - * constraint to children. - */ - if (!is_no_inherit && conform->connoinherit) - { - if (!allow_noinherit_change) - ereport(ERROR, - errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"", - NameStr(conform->conname), get_rel_name(relid))); - - conform->connoinherit = false; - retval = -1; /* caller must add constraint on child rels */ - } - - if (count > 0) - conform->coninhcount += count; - - /* sanity check */ - if (conform->coninhcount < 0) - elog(ERROR, "invalid inhcount %d for constraint \"%s\" on relation \"%s\"", - conform->coninhcount, NameStr(conform->conname), - get_rel_name(relid)); - - /* - * If the constraint is no longer inherited, mark it local. It's - * arguable that we should drop it instead, but it's hard to see that - * being better. The user can drop it manually later. - */ - if (conform->coninhcount == 0) - conform->conislocal = true; - - CatalogTupleUpdate(pg_constraint, &tup->t_self, tup); - - table_close(pg_constraint, RowExclusiveLock); - - return retval; - } - - return 0; -} - -/* - * AdjustNotNullInheritance - * Adjust not-null constraints' inhcount/islocal for - * ALTER TABLE [NO] INHERITS - * - * Mark the NOT NULL constraints for the given relation columns as - * inherited, so that they can't be dropped. - * - * Caller must have checked beforehand that attnotnull was set for all - * columns. However, some of those could be set because of a primary - * key, so throw a proper user-visible error if one is not found. - */ -void -AdjustNotNullInheritance(Oid relid, Bitmapset *columns, int count) -{ - Relation pg_constraint; - int attnum; - - pg_constraint = table_open(ConstraintRelationId, RowExclusiveLock); - - /* - * Scan the set of columns and bump inhcount for each. - */ - attnum = -1; - while ((attnum = bms_next_member(columns, attnum)) >= 0) - { - HeapTuple tup; - Form_pg_constraint conform; - - tup = findNotNullConstraintAttnum(relid, attnum); - if (!HeapTupleIsValid(tup)) - ereport(ERROR, - errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("column \"%s\" in child table must be marked NOT NULL", - get_attname(relid, attnum, - false))); - - conform = (Form_pg_constraint) GETSTRUCT(tup); - conform->coninhcount += count; - if (conform->coninhcount < 0) - elog(ERROR, "invalid inhcount %d for constraint \"%s\" on relation \"%s\"", - conform->coninhcount, NameStr(conform->conname), - get_rel_name(relid)); - - /* - * If the constraints are no longer inherited, mark them local. It's - * arguable that we should drop them instead, but it's hard to see - * that being better. The user can drop it manually later. - */ - if (conform->coninhcount == 0) - conform->conislocal = true; - - CatalogTupleUpdate(pg_constraint, &tup->t_self, tup); - } - - table_close(pg_constraint, RowExclusiveLock); -} - -/* - * RelationGetNotNullConstraints - * Return the list of not-null constraints for the given rel - * - * Caller can request cooked constraints, or raw. - * - * This is seldom needed, so we just scan pg_constraint each time. - * - * XXX This is only used to create derived tables, so NO INHERIT constraints - * are always skipped. - */ -List * -RelationGetNotNullConstraints(Oid relid, bool cooked) -{ - List *notnulls = NIL; - Relation constrRel; - HeapTuple htup; - SysScanDesc conscan; - ScanKeyData skey; - - constrRel = table_open(ConstraintRelationId, AccessShareLock); - ScanKeyInit(&skey, - Anum_pg_constraint_conrelid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(relid)); - conscan = systable_beginscan(constrRel, ConstraintRelidTypidNameIndexId, true, - NULL, 1, &skey); - - while (HeapTupleIsValid(htup = systable_getnext(conscan))) - { - Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(htup); - AttrNumber colnum; - - if (conForm->contype != CONSTRAINT_NOTNULL) - continue; - if (conForm->connoinherit) - continue; - - colnum = extractNotNullColumn(htup); - - if (cooked) - { - CookedConstraint *cooked; - - cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint)); - - cooked->contype = CONSTR_NOTNULL; - cooked->name = pstrdup(NameStr(conForm->conname)); - cooked->attnum = colnum; - cooked->expr = NULL; - cooked->skip_validation = false; - cooked->is_local = true; - cooked->inhcount = 0; - cooked->is_no_inherit = conForm->connoinherit; - - notnulls = lappend(notnulls, cooked); - } - else - { - Constraint *constr; - - constr = makeNode(Constraint); - constr->contype = CONSTR_NOTNULL; - constr->conname = pstrdup(NameStr(conForm->conname)); - constr->deferrable = false; - constr->initdeferred = false; - constr->location = -1; - constr->keys = list_make1(makeString(get_attname(relid, colnum, - false))); - constr->skip_validation = false; - constr->initially_valid = true; - notnulls = lappend(notnulls, constr); - } - } - - systable_endscan(conscan); - table_close(constrRel, AccessShareLock); - - return notnulls; -} - - -/* * Delete a single constraint record. */ void @@ -941,8 +616,6 @@ RemoveConstraintById(Oid conId) Relation conDesc; HeapTuple tup; Form_pg_constraint con; - bool dropping_pk = false; - List *unconstrained_cols = NIL; conDesc = table_open(ConstraintRelationId, RowExclusiveLock); @@ -967,9 +640,7 @@ 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. For not-null and primary key constraints, - * obtain the list of columns affected, so that we can reset their - * attnotnull flags below. + * entries when we commit. */ if (con->contype == CONSTRAINT_CHECK) { @@ -996,36 +667,6 @@ 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); @@ -1045,86 +686,6 @@ 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); |