From 7124a7715d9c34b1452a8895b5af933a0d2cd161 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Mon, 18 Jan 2021 15:56:02 +0300 Subject: [PATCH] Skip 'already exists' in CREATE TABLE IF NOT EXISTS PARTITION OF (#4507) * Just skip 'already exists' in CT IF NOT EXISTS PARTITION OF * Generalize to tables that are not already distributed partitions --- src/backend/distributed/commands/table.c | 29 +++++++++++++++- .../regress/expected/multi_partitioning.out | 33 ++++++++++++++++++- src/test/regress/sql/multi_partitioning.sql | 25 +++++++++++++- 3 files changed, 84 insertions(+), 3 deletions(-) diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index 60c59ba32..7273267a9 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -283,13 +283,40 @@ PostprocessCreateTableStmtPartitionOf(CreateStmt *createStatement, const Assert(parentRelationId != InvalidOid); + Oid relationId = RangeVarGetRelid(createStatement->relation, NoLock, missingOk); + + /* + * In case of an IF NOT EXISTS statement, Postgres lets it pass through the + * standardProcess_Utility, and gets into this Post-process hook by + * ignoring the statement if the table already exists. Thus, we need to make + * sure Citus behaves like plain PG in case the relation already exists. + */ + if (createStatement->if_not_exists) + { + if (IsCitusTable(relationId)) + { + /* + * Ignore if the relation is already distributed. + */ + return; + } + else if (!PartitionTable(relationId) || + PartitionParentOid(relationId) != parentRelationId) + { + /* + * Ignore if the relation is not a partition, or if that + * partition's parent is not the current parent from parentRelationId + */ + return; + } + } + /* * If a partition is being created and if its parent is a distributed * table, we will distribute this table as well. */ if (IsCitusTable(parentRelationId)) { - Oid relationId = RangeVarGetRelid(createStatement->relation, NoLock, missingOk); Var *parentDistributionColumn = DistPartitionKeyOrError(parentRelationId); char parentDistributionMethod = DISTRIBUTE_BY_HASH; char *parentRelationName = generate_qualified_relation_name(parentRelationId); diff --git a/src/test/regress/expected/multi_partitioning.out b/src/test/regress/expected/multi_partitioning.out index 8873e64d8..9903ef3e2 100644 --- a/src/test/regress/expected/multi_partitioning.out +++ b/src/test/regress/expected/multi_partitioning.out @@ -1958,12 +1958,43 @@ SELECT parent_table, partition_column, partition, from_value, to_value FROM time public.non_distributed_partitioned_table | a | public.non_distributed_partitioned_table_1 | 0 | 10 (6 rows) +-- create the same partition to verify it behaves like in plain PG +CREATE TABLE partitioning_test_2011 PARTITION OF partitioning_test FOR VALUES FROM ('2011-01-01') TO ('2012-01-01'); +ERROR: relation "partitioning_test_2011" already exists +CREATE TABLE IF NOT EXISTS partitioning_test_2011 PARTITION OF partitioning_test FOR VALUES FROM ('2011-01-01') TO ('2012-01-01'); +NOTICE: relation "partitioning_test_2011" already exists, skipping +-- verify we can create a partition that doesn't already exist with IF NOT EXISTS +CREATE TABLE IF NOT EXISTS partitioning_test_2013 PARTITION OF partitioning_test FOR VALUES FROM ('2013-01-01') TO ('2014-01-01'); +SELECT logicalrelid FROM pg_dist_partition WHERE logicalrelid IN ('partitioning_test', 'partitioning_test_2013') ORDER BY 1; + logicalrelid +--------------------------------------------------------------------- + partitioning_test + partitioning_test_2013 +(2 rows) + +-- create the same table but that is not a partition and verify it behaves like in plain PG +CREATE TABLE not_partition(time date); +CREATE TABLE not_partition PARTITION OF partitioning_test FOR VALUES FROM ('2011-01-01') TO ('2012-01-01'); +ERROR: relation "not_partition" already exists +CREATE TABLE IF NOT EXISTS not_partition PARTITION OF partitioning_test FOR VALUES FROM ('2011-01-01') TO ('2012-01-01'); +NOTICE: relation "not_partition" already exists, skipping +DROP TABLE not_partition; +-- verify it skips when the partition with the same name belongs to another table +CREATE TABLE another_table(id int, time date) PARTITION BY RANGE (time); +CREATE TABLE partition_of_other_table PARTITION OF another_table FOR VALUES FROM ('2014-01-01') TO ('2015-01-01'); +CREATE TABLE partition_of_other_table PARTITION OF partitioning_test FOR VALUES FROM ('2014-01-01') TO ('2015-01-01'); +ERROR: relation "partition_of_other_table" already exists +CREATE TABLE IF NOT EXISTS partition_of_other_table PARTITION OF partitioning_test FOR VALUES FROM ('2014-01-01') TO ('2015-01-01'); +NOTICE: relation "partition_of_other_table" already exists, skipping +ALTER TABLE another_table DETACH PARTITION partition_of_other_table; +DROP TABLE another_table, partition_of_other_table; ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2008; ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2009; ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2010; ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2011; +ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2013; DROP TABLE partitioning_test, partitioning_test_2008, partitioning_test_2009, - partitioning_test_2010, partitioning_test_2011, + partitioning_test_2010, partitioning_test_2011, partitioning_test_2013, reference_table, reference_table_2; RESET SEARCH_PATH; -- not timestamp partitioned diff --git a/src/test/regress/sql/multi_partitioning.sql b/src/test/regress/sql/multi_partitioning.sql index 57b69a6a4..9227651d0 100644 --- a/src/test/regress/sql/multi_partitioning.sql +++ b/src/test/regress/sql/multi_partitioning.sql @@ -1155,13 +1155,36 @@ ALTER TABLE partitioning_test ATTACH PARTITION partitioning_test_2011 SELECT parent_table, partition_column, partition, from_value, to_value FROM time_partitions; +-- create the same partition to verify it behaves like in plain PG +CREATE TABLE partitioning_test_2011 PARTITION OF partitioning_test FOR VALUES FROM ('2011-01-01') TO ('2012-01-01'); +CREATE TABLE IF NOT EXISTS partitioning_test_2011 PARTITION OF partitioning_test FOR VALUES FROM ('2011-01-01') TO ('2012-01-01'); + +-- verify we can create a partition that doesn't already exist with IF NOT EXISTS +CREATE TABLE IF NOT EXISTS partitioning_test_2013 PARTITION OF partitioning_test FOR VALUES FROM ('2013-01-01') TO ('2014-01-01'); +SELECT logicalrelid FROM pg_dist_partition WHERE logicalrelid IN ('partitioning_test', 'partitioning_test_2013') ORDER BY 1; + +-- create the same table but that is not a partition and verify it behaves like in plain PG +CREATE TABLE not_partition(time date); +CREATE TABLE not_partition PARTITION OF partitioning_test FOR VALUES FROM ('2011-01-01') TO ('2012-01-01'); +CREATE TABLE IF NOT EXISTS not_partition PARTITION OF partitioning_test FOR VALUES FROM ('2011-01-01') TO ('2012-01-01'); +DROP TABLE not_partition; + +-- verify it skips when the partition with the same name belongs to another table +CREATE TABLE another_table(id int, time date) PARTITION BY RANGE (time); +CREATE TABLE partition_of_other_table PARTITION OF another_table FOR VALUES FROM ('2014-01-01') TO ('2015-01-01'); +CREATE TABLE partition_of_other_table PARTITION OF partitioning_test FOR VALUES FROM ('2014-01-01') TO ('2015-01-01'); +CREATE TABLE IF NOT EXISTS partition_of_other_table PARTITION OF partitioning_test FOR VALUES FROM ('2014-01-01') TO ('2015-01-01'); +ALTER TABLE another_table DETACH PARTITION partition_of_other_table; +DROP TABLE another_table, partition_of_other_table; + ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2008; ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2009; ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2010; ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2011; +ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2013; DROP TABLE partitioning_test, partitioning_test_2008, partitioning_test_2009, - partitioning_test_2010, partitioning_test_2011, + partitioning_test_2010, partitioning_test_2011, partitioning_test_2013, reference_table, reference_table_2; RESET SEARCH_PATH;