mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
Fix ALTER TABLE ONLY .. DROP CONSTRAINT.
When I consolidated two copies of the HOT-chain search logic in commit 4da99ea4231e3d8bbf28b666748c1028e7b7d665, I introduced a behavior change: the old code wouldn't necessarily traverse the entire chain, if the most recently returned tuple were updated while the HOT chain traversal is in progress. The new behavior seems more correct, but unfortunately, the code here relies on a scan with SnapshotNow failing to see its own updates. That seems pretty shaky even with the old HOT chain traversal behavior, since there's no guarantee that these updates will always be HOT, but it's trivial to broke a failure with the new HOT search logic. Fix by updating just the first matching pg_constraint tuple, rather than all of them, since there should be only one anyway. But since nobody has reproduced this failure on older versions, no back-patch for now. Report and test case by Alex Hunsaker; tablecmds.c changes by me.
This commit is contained in:
parent
c980426c69
commit
c0f03aae04
@ -6734,6 +6734,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
|
||||
{
|
||||
Oid childrelid = lfirst_oid(child);
|
||||
Relation childrel;
|
||||
HeapTuple copy_tuple;
|
||||
|
||||
/* find_inheritance_children already got lock */
|
||||
childrel = heap_open(childrelid, NoLock);
|
||||
@ -6746,30 +6747,36 @@ ATExecDropConstraint(Relation rel, const char *constrName,
|
||||
scan = systable_beginscan(conrel, ConstraintRelidIndexId,
|
||||
true, SnapshotNow, 1, &key);
|
||||
|
||||
found = false;
|
||||
|
||||
/* scan for matching tuple - there should only be one */
|
||||
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
|
||||
{
|
||||
HeapTuple copy_tuple;
|
||||
|
||||
con = (Form_pg_constraint) GETSTRUCT(tuple);
|
||||
|
||||
/* Right now only CHECK constraints can be inherited */
|
||||
if (con->contype != CONSTRAINT_CHECK)
|
||||
continue;
|
||||
|
||||
if (strcmp(NameStr(con->conname), constrName) != 0)
|
||||
continue;
|
||||
if (strcmp(NameStr(con->conname), constrName) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
found = true;
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("constraint \"%s\" of relation \"%s\" does not exist",
|
||||
constrName,
|
||||
RelationGetRelationName(childrel))));
|
||||
|
||||
copy_tuple = heap_copytuple(tuple);
|
||||
|
||||
systable_endscan(scan);
|
||||
|
||||
con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
|
||||
|
||||
if (con->coninhcount <= 0) /* shouldn't happen */
|
||||
elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
|
||||
childrelid, constrName);
|
||||
|
||||
copy_tuple = heap_copytuple(tuple);
|
||||
con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
|
||||
|
||||
if (recurse)
|
||||
{
|
||||
/*
|
||||
@ -6813,16 +6820,6 @@ ATExecDropConstraint(Relation rel, const char *constrName,
|
||||
}
|
||||
|
||||
heap_freetuple(copy_tuple);
|
||||
}
|
||||
|
||||
systable_endscan(scan);
|
||||
|
||||
if (!found)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("constraint \"%s\" of relation \"%s\" does not exist",
|
||||
constrName,
|
||||
RelationGetRelationName(childrel))));
|
||||
|
||||
heap_close(childrel, NoLock);
|
||||
}
|
||||
|
@ -2103,3 +2103,12 @@ ALTER TABLE tt7 NOT OF;
|
||||
x | integer |
|
||||
y | numeric(8,2) |
|
||||
|
||||
-- make sure we can drop a constraint on the parent but it remains on the child
|
||||
CREATE TABLE test_drop_constr_parent (c text CHECK (c IS NOT NULL));
|
||||
CREATE TABLE test_drop_constr_child () INHERITS (test_drop_constr_parent);
|
||||
ALTER TABLE ONLY test_drop_constr_parent DROP CONSTRAINT "test_drop_constr_parent_c_check";
|
||||
-- should fail
|
||||
INSERT INTO test_drop_constr_child (c) VALUES (NULL);
|
||||
ERROR: new row for relation "test_drop_constr_child" violates check constraint "test_drop_constr_parent_c_check"
|
||||
DROP TABLE test_drop_constr_parent CASCADE;
|
||||
NOTICE: drop cascades to table test_drop_constr_child
|
||||
|
@ -1456,3 +1456,11 @@ CREATE TYPE tt_t1 AS (x int, y numeric(8,2));
|
||||
ALTER TABLE tt7 OF tt_t1; -- reassign an already-typed table
|
||||
ALTER TABLE tt7 NOT OF;
|
||||
\d tt7
|
||||
|
||||
-- make sure we can drop a constraint on the parent but it remains on the child
|
||||
CREATE TABLE test_drop_constr_parent (c text CHECK (c IS NOT NULL));
|
||||
CREATE TABLE test_drop_constr_child () INHERITS (test_drop_constr_parent);
|
||||
ALTER TABLE ONLY test_drop_constr_parent DROP CONSTRAINT "test_drop_constr_parent_c_check";
|
||||
-- should fail
|
||||
INSERT INTO test_drop_constr_child (c) VALUES (NULL);
|
||||
DROP TABLE test_drop_constr_parent CASCADE;
|
||||
|
Loading…
x
Reference in New Issue
Block a user