aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorAlvaro Herrera <alvherre@alvh.no-ip.org>2020-02-07 18:27:18 -0300
committerAlvaro Herrera <alvherre@alvh.no-ip.org>2020-02-07 18:27:18 -0300
commit2c80a656c2b5a2731e5df3c9398a66ffc18cbcf3 (patch)
tree0445fd298ff830d31fe7134cfab5ab32be938324 /src/backend
parentce054a8cd4f4faf3479050feb5d8fa08545f4c5c (diff)
downloadpostgresql-2c80a656c2b5a2731e5df3c9398a66ffc18cbcf3.tar.gz
postgresql-2c80a656c2b5a2731e5df3c9398a66ffc18cbcf3.zip
Fix failure to create FKs correctly in partitions
On a multi-level partioned table, when adding a partition not directly connected to the root table, foreign key constraints referencing the root were not cloned to the new partition, leading to the FK being possibly inadvertently violated later on. This was caused by fuzzy thinking in CloneFkReferenced (commit f56f8f8da6af): it was skipping constraints marked as having parents on the theory that cloning those would create duplicates; but that's only correct for the top level of the partitioning hierarchy. For levels below that one, such constraints must still be considered and only skipped if later on we see that we'd create duplicates. Apparently, I (Álvaro) wrote the comments right but the code implemented something slightly different. Author: Jehan-Guillaume de Rorthais Discussion: https://postgr.es/m/20200206004948.238352db@firost
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/commands/tablecmds.c28
1 files changed, 17 insertions, 11 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f3d64c6a9ec..d07476d8ddf 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8549,13 +8549,13 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
List *clone = NIL;
/*
- * Search for any constraints where this partition is in the referenced
- * side. However, we must ignore any constraint whose parent constraint
- * is also going to be cloned, to avoid duplicates. So do it in two
- * steps: first construct the list of constraints to clone, then go over
- * that list cloning those whose parents are not in the list. (We must
- * not rely on the parent being seen first, since the catalog scan could
- * return children first.)
+ * Search for any constraints where this partition's parent is in the
+ * referenced side. However, we must not clone any constraint whose
+ * parent constraint is also going to be cloned, to avoid duplicates. So
+ * do it in two steps: first construct the list of constraints to clone,
+ * then go over that list cloning those whose parents are not in the list.
+ * (We must not rely on the parent being seen first, since the catalog
+ * scan could return children first.)
*/
pg_constraint = table_open(ConstraintRelationId, RowShareLock);
ScanKeyInit(&key[0],
@@ -8571,10 +8571,6 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
{
Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
- /* Only try to clone the top-level constraint; skip child ones. */
- if (constrForm->conparentid != InvalidOid)
- continue;
-
clone = lappend_oid(clone, constrForm->oid);
}
systable_endscan(scan);
@@ -8605,6 +8601,16 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
/*
+ * As explained above: don't try to clone a constraint for which we're
+ * going to clone the parent.
+ */
+ if (list_member_oid(clone, constrForm->conparentid))
+ {
+ ReleaseSysCache(tuple);
+ continue;
+ }
+
+ /*
* Because we're only expanding the key space at the referenced side,
* we don't need to prevent any operation in the referencing table, so
* AccessShareLock suffices (assumes that dropping the constraint