delay_altertable_foreignkey_validation_during_table_rewrite.patch
application/octet-stream
Filename: delay_altertable_foreignkey_validation_during_table_rewrite.patch
Type: application/octet-stream
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/commands/tablecmds.c | 51 | 21 |
| src/test/regress/expected/alter_table.out | 6 | 0 |
| src/test/regress/sql/alter_table.sql | 7 | 0 |
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f79044f39f..f64dfffef0 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -328,7 +328,8 @@ static void AlterSeqNamespaces(Relation classRel, Relation rel,
LOCKMODE lockmode);
static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
bool recurse, bool recursing, LOCKMODE lockmode);
-static ObjectAddress ATExecValidateConstraint(Relation rel, char *constrName,
+static ObjectAddress ATExecValidateConstraint(AlteredTableInfo *tab,
+ Relation rel, char *constrName,
bool recurse, bool recursing, LOCKMODE lockmode);
static int transformColumnNameList(Oid relId, List *colList,
int16 *attnums, Oid *atttypids);
@@ -4500,13 +4501,13 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
break;
case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
- address = ATExecValidateConstraint(rel, cmd->name, false, false,
- lockmode);
+ address = ATExecValidateConstraint(tab, rel, cmd->name, false,
+ false, lockmode);
break;
case AT_ValidateConstraintRecurse: /* VALIDATE CONSTRAINT with
* recursion */
- address = ATExecValidateConstraint(rel, cmd->name, true, false,
- lockmode);
+ address = ATExecValidateConstraint(tab, rel, cmd->name, true,
+ false, lockmode);
break;
case AT_DropConstraint: /* DROP CONSTRAINT */
ATExecDropConstraint(rel, cmd->name, cmd->behavior,
@@ -9727,8 +9728,9 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
* was already validated, InvalidObjectAddress is returned.
*/
static ObjectAddress
-ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
- bool recursing, LOCKMODE lockmode)
+ATExecValidateConstraint(AlteredTableInfo *tab, Relation rel,
+ char *constrName, bool recurse, bool recursing,
+ LOCKMODE lockmode)
{
Relation conrel;
SysScanDesc scan;
@@ -9779,22 +9781,50 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
if (con->contype == CONSTRAINT_FOREIGN)
{
- Relation refrel;
-
/*
- * Triggers are already in place on both tables, so a concurrent
- * write that alters the result here is not possible. Normally we
- * can run a query here to do the validation, which would only
- * require AccessShareLock. In some cases, it is possible that we
- * might need to fire triggers to perform the check, so we take a
- * lock at RowShareLock level just in case.
+ * If a table rewrite is pending then we may not be able to
+ * perform a foreign key constriant validation right now. We'll
+ * just mark that the validation must be done after the rewrite
+ * instead.
*/
- refrel = table_open(con->confrelid, RowShareLock);
+ if (tab->rewrite > 0)
+ {
+ NewConstraint *newcon;
+ Constraint *fkconstraint;
+
+ fkconstraint = makeNode(Constraint);
+ /* for now this is all we need */
+ fkconstraint->conname = constrName;
+
+ newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
+ newcon->name = constrName;
+ newcon->contype = CONSTR_FOREIGN;
+ newcon->refrelid = con->confrelid;
+ newcon->refindid = con->conindid;
+ newcon->conid = con->oid;
+ newcon->qual = (Node *) fkconstraint;
+
+ tab->constraints = lappend(tab->constraints, newcon);
+ }
+ else
+ {
+ Relation refrel;
- validateForeignKeyConstraint(constrName, rel, refrel,
- con->conindid,
- con->oid);
- table_close(refrel, NoLock);
+ /*
+ * Triggers are already in place on both tables, so a concurrent
+ * write that alters the result here is not possible. Normally we
+ * can run a query here to do the validation, which would only
+ * require AccessShareLock. In some cases, it is possible that we
+ * might need to fire triggers to perform the check, so we take a
+ * lock at RowShareLock level just in case.
+ */
+ refrel = table_open(con->confrelid, RowShareLock);
+
+ validateForeignKeyConstraint(constrName, rel, refrel,
+ con->conindid,
+ con->oid);
+ table_close(refrel, NoLock);
+ }
/*
* We disallow creating invalid foreign keys to or from
@@ -9844,7 +9874,7 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
/* find_all_inheritors already got lock */
childrel = table_open(childoid, NoLock);
- ATExecValidateConstraint(childrel, constrName, false,
+ ATExecValidateConstraint(tab, childrel, constrName, false,
true, lockmode);
table_close(childrel, NoLock);
}
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 002079601f..a3b8e7c044 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -997,6 +997,12 @@ alter table atacc1
add column b float8 not null default random(),
add primary key(a);
drop table atacc1;
+-- additionally, we've seen issues with foreign key validation not being
+-- properly delayed until after a table rewrite. Check that works ok.
+create table atacc1 (a int primary key);
+alter table atacc1 add constraint atacc1_fkey foreign key (a) references atacc1 (a) not valid;
+alter table atacc1 validate constraint atacc1_fkey, alter A type bigint;
+drop table atacc1;
-- something a little more complicated
create table atacc1 ( test int, test2 int);
-- add a primary key constraint
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index ec272d78f8..6f9091060b 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -757,6 +757,13 @@ alter table atacc1
add primary key(a);
drop table atacc1;
+-- additionally, we've seen issues with foreign key validation not being
+-- properly delayed until after a table rewrite. Check that works ok.
+create table atacc1 (a int primary key);
+alter table atacc1 add constraint atacc1_fkey foreign key (a) references atacc1 (a) not valid;
+alter table atacc1 validate constraint atacc1_fkey, alter A type bigint;
+drop table atacc1;
+
-- something a little more complicated
create table atacc1 ( test int, test2 int);
-- add a primary key constraint