From 3df578010e3098b9b3eb22d5743bed88e5d38863 Mon Sep 17 00:00:00 2001 From: SaitTalhaNisanci Date: Mon, 23 Mar 2020 13:22:24 +0300 Subject: [PATCH] add a UDF to update colocation (#3623) If two tables have the same distribution column type, we implicitly colocate them. This is useful since colocation has a big performance impact in most applications. When a table is rebalanced, all of the colocated tables are also rebalanced. If table A and table B are colocated and we want to rebalance table A, table B will also be rebalanced. We need replica identity so that logical replication can replicate updates and deletes during rebalancing. If table B does not have a replica identity we error out. A solution to this is to introduce a UDF so that colocation can be updated. The remaining tables in the colocation group will stay colocated. For example if table A, B and C are colocated and after updating table B's colocations, table A and table C stay colocated. The "updating colocation" step does not move any data around, it only updated pg_dist_partition and pg_dist_colocation tables. Specifically it creates a new colocation group for the table and updates the entry in pg_dist_partition while invalidating any cache. --- .../commands/create_distributed_table.c | 2 +- .../master/master_metadata_utility.c | 33 ++ .../distributed/sql/citus--9.2-2--9.3-1.sql | 1 + .../9.3-1.sql | 6 + .../latest.sql | 6 + .../distributed/utils/colocation_utils.c | 121 +++++- src/include/distributed/colocation_utils.h | 1 + .../distributed/master_metadata_utility.h | 2 + .../expected/multi_colocation_utils.out | 411 ++++++++++++++++++ .../regress/sql/multi_colocation_utils.sql | 129 ++++++ 10 files changed, 692 insertions(+), 20 deletions(-) create mode 100644 src/backend/distributed/sql/udfs/update_distributed_table_colocation/9.3-1.sql create mode 100644 src/backend/distributed/sql/udfs/update_distributed_table_colocation/latest.sql diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index 60caa58a5..5bfbb73af 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -574,7 +574,7 @@ ColocationIdForNewTable(Oid relationId, Var *distributionColumn, createdColocationGroup = true; } } - else if (pg_strncasecmp(colocateWithTableName, "none", NAMEDATALEN) == 0) + else if (IsColocateWithNone(colocateWithTableName)) { colocationId = GetNextColocationId(); diff --git a/src/backend/distributed/master/master_metadata_utility.c b/src/backend/distributed/master/master_metadata_utility.c index 196a33b97..89b54069a 100644 --- a/src/backend/distributed/master/master_metadata_utility.c +++ b/src/backend/distributed/master/master_metadata_utility.c @@ -1389,6 +1389,39 @@ EnsureFunctionOwner(Oid functionId) } +/* + * EnsureHashDistributedTable error out if the given relation is not a hash distributed table + * with the given message. + */ +void +EnsureHashDistributedTable(Oid relationId) +{ + if (!IsHashDistributedTable(relationId)) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("relation %s should be a " + "hash distributed table", get_rel_name(relationId)))); + } +} + + +/* + * IsDistributedTable returns true if the given relation is + * a distributed table. + */ +bool +IsHashDistributedTable(Oid relationId) +{ + CitusTableCacheEntry *sourceTableEntry = GetCitusTableCacheEntry(relationId); + if (sourceTableEntry == NULL) + { + return false; + } + char sourceDistributionMethod = sourceTableEntry->partitionMethod; + return sourceDistributionMethod == DISTRIBUTE_BY_HASH; +} + + /* * EnsureSuperUser check that the current user is a superuser and errors out if not. */ diff --git a/src/backend/distributed/sql/citus--9.2-2--9.3-1.sql b/src/backend/distributed/sql/citus--9.2-2--9.3-1.sql index 58feeed42..53f02678e 100644 --- a/src/backend/distributed/sql/citus--9.2-2--9.3-1.sql +++ b/src/backend/distributed/sql/citus--9.2-2--9.3-1.sql @@ -3,3 +3,4 @@ /* bump version to 9.3-1 */ #include "udfs/citus_extradata_container/9.3-1.sql" +#include "udfs/update_distributed_table_colocation/9.3-1.sql" diff --git a/src/backend/distributed/sql/udfs/update_distributed_table_colocation/9.3-1.sql b/src/backend/distributed/sql/udfs/update_distributed_table_colocation/9.3-1.sql new file mode 100644 index 000000000..3afdcc81f --- /dev/null +++ b/src/backend/distributed/sql/udfs/update_distributed_table_colocation/9.3-1.sql @@ -0,0 +1,6 @@ +CREATE OR REPLACE FUNCTION pg_catalog.update_distributed_table_colocation(table_name regclass, colocate_with text) + RETURNS void + LANGUAGE C STRICT + AS 'MODULE_PATHNAME', $$update_distributed_table_colocation$$; +COMMENT ON FUNCTION pg_catalog.update_distributed_table_colocation(table_name regclass, colocate_with text) + IS 'updates colocation of a table'; diff --git a/src/backend/distributed/sql/udfs/update_distributed_table_colocation/latest.sql b/src/backend/distributed/sql/udfs/update_distributed_table_colocation/latest.sql new file mode 100644 index 000000000..3afdcc81f --- /dev/null +++ b/src/backend/distributed/sql/udfs/update_distributed_table_colocation/latest.sql @@ -0,0 +1,6 @@ +CREATE OR REPLACE FUNCTION pg_catalog.update_distributed_table_colocation(table_name regclass, colocate_with text) + RETURNS void + LANGUAGE C STRICT + AS 'MODULE_PATHNAME', $$update_distributed_table_colocation$$; +COMMENT ON FUNCTION pg_catalog.update_distributed_table_colocation(table_name regclass, colocate_with text) + IS 'updates colocation of a table'; diff --git a/src/backend/distributed/utils/colocation_utils.c b/src/backend/distributed/utils/colocation_utils.c index 21423bc66..2c60e6e9b 100644 --- a/src/backend/distributed/utils/colocation_utils.c +++ b/src/backend/distributed/utils/colocation_utils.c @@ -50,11 +50,13 @@ static int CompareShardPlacementsByNode(const void *leftElement, static void UpdateRelationColocationGroup(Oid distributedRelationId, uint32 colocationId); static List * ColocationGroupTableList(Oid colocationId); static void DeleteColocationGroup(uint32 colocationId); - +static uint32 CreateColocationGroupForRelation(Oid sourceRelationId); +static void BreakColocation(Oid sourceRelationId); /* exports for SQL callable functions */ PG_FUNCTION_INFO_V1(mark_tables_colocated); PG_FUNCTION_INFO_V1(get_colocated_shard_array); +PG_FUNCTION_INFO_V1(update_distributed_table_colocation); /* @@ -96,6 +98,74 @@ mark_tables_colocated(PG_FUNCTION_ARGS) } +/* + * update_distributed_table_colocation updates the colocation of a table. + * if colocate_with -> 'none' then the table is assigned a new + * colocation group. + */ +Datum +update_distributed_table_colocation(PG_FUNCTION_ARGS) +{ + Oid targetRelationId = PG_GETARG_OID(0); + text *colocateWithTableNameText = PG_GETARG_TEXT_P(1); + + CheckCitusVersion(ERROR); + EnsureCoordinator(); + EnsureTableOwner(targetRelationId); + EnsureHashDistributedTable(targetRelationId); + + char *colocateWithTableName = text_to_cstring(colocateWithTableNameText); + if (IsColocateWithNone(colocateWithTableName)) + { + BreakColocation(targetRelationId); + } + else + { + Oid colocateWithTableId = ResolveRelationId(colocateWithTableNameText, false); + EnsureHashDistributedTable(colocateWithTableId); + MarkTablesColocated(colocateWithTableId, targetRelationId); + } + PG_RETURN_VOID(); +} + + +/* + * IsColocateWithNone returns true if the given table is + * the special keyword "none". + */ +bool +IsColocateWithNone(char *colocateWithTableName) +{ + return pg_strncasecmp(colocateWithTableName, "none", NAMEDATALEN) == 0; +} + + +/* + * BreakColocation breaks the colocations of the given relation id. + * If t1, t2 and t3 are colocated and we call this function with t2, + * t1 and t3 will stay colocated but t2 will have a new colocation id. + * Note that this function does not move any data around for the new colocation. + */ +static void +BreakColocation(Oid sourceRelationId) +{ + /* + * Get an exclusive lock on the colocation system catalog. Therefore, we + * can be sure that there will no modifications on the colocation table + * until this transaction is committed. + */ + Relation pgDistColocation = heap_open(DistColocationRelationId(), ExclusiveLock); + + uint32 newColocationId = GetNextColocationId(); + UpdateRelationColocationGroup(sourceRelationId, newColocationId); + + /* if there is not any remaining table in the colocation group, delete it */ + DeleteColocationGroupIfNoTablesBelong(sourceRelationId); + + heap_close(pgDistColocation, NoLock); +} + + /* * get_colocated_shards_array returns array of shards ids which are co-located with given * shard. @@ -131,6 +201,36 @@ get_colocated_shard_array(PG_FUNCTION_ARGS) } +/* + * CreateColocationGroupForRelation creates colocation entry in + * pg_dist_colocation and updated the colocation id in pg_dist_partition + * for the given relation. + */ +static uint32 +CreateColocationGroupForRelation(Oid sourceRelationId) +{ + uint32 shardCount = ShardIntervalCount(sourceRelationId); + uint32 shardReplicationFactor = TableShardReplicationFactor(sourceRelationId); + + Var *sourceDistributionColumn = DistPartitionKey(sourceRelationId); + Oid sourceDistributionColumnType = InvalidOid; + Oid sourceDistributionColumnCollation = InvalidOid; + + /* reference tables has NULL distribution column */ + if (sourceDistributionColumn != NULL) + { + sourceDistributionColumnType = sourceDistributionColumn->vartype; + sourceDistributionColumnCollation = sourceDistributionColumn->varcollid; + } + + uint32 sourceColocationId = CreateColocationGroup(shardCount, shardReplicationFactor, + sourceDistributionColumnType, + sourceDistributionColumnCollation); + UpdateRelationColocationGroup(sourceRelationId, sourceColocationId); + return sourceColocationId; +} + + /* * MarkTablesColocated puts both tables to same colocation group. If the * source table is in INVALID_COLOCATION_ID group, then it creates a new @@ -160,24 +260,7 @@ MarkTablesColocated(Oid sourceRelationId, Oid targetRelationId) uint32 sourceColocationId = TableColocationId(sourceRelationId); if (sourceColocationId == INVALID_COLOCATION_ID) { - uint32 shardCount = ShardIntervalCount(sourceRelationId); - uint32 shardReplicationFactor = TableShardReplicationFactor(sourceRelationId); - - Var *sourceDistributionColumn = DistPartitionKey(sourceRelationId); - Oid sourceDistributionColumnType = InvalidOid; - Oid sourceDistributionColumnCollation = InvalidOid; - - /* reference tables has NULL distribution column */ - if (sourceDistributionColumn != NULL) - { - sourceDistributionColumnType = sourceDistributionColumn->vartype; - sourceDistributionColumnCollation = sourceDistributionColumn->varcollid; - } - - sourceColocationId = CreateColocationGroup(shardCount, shardReplicationFactor, - sourceDistributionColumnType, - sourceDistributionColumnCollation); - UpdateRelationColocationGroup(sourceRelationId, sourceColocationId); + sourceColocationId = CreateColocationGroupForRelation(sourceRelationId); } uint32 targetColocationId = TableColocationId(targetRelationId); diff --git a/src/include/distributed/colocation_utils.h b/src/include/distributed/colocation_utils.h index 7eea3ecb8..2ac89dbbc 100644 --- a/src/include/distributed/colocation_utils.h +++ b/src/include/distributed/colocation_utils.h @@ -30,6 +30,7 @@ uint32 ColocationId(int shardCount, int replicationFactor, Oid distributionColum extern uint32 CreateColocationGroup(int shardCount, int replicationFactor, Oid distributionColumnType, Oid distributionColumnCollation); +extern bool IsColocateWithNone(char *colocateWithTableName); extern uint32 GetNextColocationId(void); extern void CheckReplicationModel(Oid sourceRelationId, Oid targetRelationId); extern void CheckDistributionColumnType(Oid sourceRelationId, Oid targetRelationId); diff --git a/src/include/distributed/master_metadata_utility.h b/src/include/distributed/master_metadata_utility.h index ca0d60f7c..4c3c2b5ac 100644 --- a/src/include/distributed/master_metadata_utility.h +++ b/src/include/distributed/master_metadata_utility.h @@ -148,6 +148,8 @@ extern char * TableOwner(Oid relationId); extern void EnsureTablePermissions(Oid relationId, AclMode mode); extern void EnsureTableOwner(Oid relationId); extern void EnsureSchemaOwner(Oid schemaId); +extern void EnsureHashDistributedTable(Oid relationId); +extern bool IsHashDistributedTable(Oid relationId); extern void EnsureSequenceOwner(Oid sequenceOid); extern void EnsureFunctionOwner(Oid functionId); extern void EnsureSuperUser(void); diff --git a/src/test/regress/expected/multi_colocation_utils.out b/src/test/regress/expected/multi_colocation_utils.out index 7a5120354..26bfc21ba 100644 --- a/src/test/regress/expected/multi_colocation_utils.out +++ b/src/test/regress/expected/multi_colocation_utils.out @@ -1032,6 +1032,407 @@ SELECT create_distributed_table('table2_groupG', 'id', colocate_with => 'NONE'); SELECT mark_tables_colocated('table1_groupG', ARRAY['table2_groupG']); ERROR: cannot colocate tables table1_groupg and table2_groupg DETAIL: Replication models don't match for table1_groupg and table2_groupg. +CREATE TABLE d1(a int, b int); +CREATE TABLE d2(a int, b int); +CREATE TABLE d3(a int, b int); +CREATE TABLE d4(a int, b int); +CREATE TABLE different_d1(ch char); +CREATE TABLE append_table(a int, b int); +CREATE TABLE range_table(a int, b int); +-- special keyword none +CREATE TABLE none(a int, b int); +CREATE TABLE ref(a int); +CREATE TABLE local_table(a int); +SELECT create_distributed_table('d1', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('d2', 'a', colocate_with => 'd1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('d3', 'a', colocate_with => 'd2'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('d4', 'a', colocate_with => 'd3'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('none', 'a', colocate_with => 'd4'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('different_d1', 'ch'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('append_table', 'a', 'append'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('range_table', 'a', 'range'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_reference_table('ref'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +-- check d1, d2, d3 and d4 has the same colocation id => they are colocated. +SELECT get_table_colocation_id('d1'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT get_table_colocation_id('d2'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT get_table_colocation_id('d3'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT get_table_colocation_id('d4'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT tables_colocated('d1', 'd2'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +SELECT tables_colocated('d2', 'd3'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +SELECT tables_colocated('d2', 'd4'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +SELECT tables_colocated('d3', 'd4'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +SELECT tables_colocated('d1', 'd3'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +SELECT tables_colocated('d1', 'd4'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +-- break colocation of d2 +SELECT update_distributed_table_colocation('d2', colocate_with => 'none'); + update_distributed_table_colocation +--------------------------------------------------------------------- + +(1 row) + +-- d1 and d3 and d4 should be colocated, d2 should have a new colocation id. +SELECT get_table_colocation_id('d1'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT get_table_colocation_id('d2'); + get_table_colocation_id +--------------------------------------------------------------------- + 11 +(1 row) + +SELECT get_table_colocation_id('d3'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT get_table_colocation_id('d4'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT tables_colocated('d1', 'd2'); + tables_colocated +--------------------------------------------------------------------- + f +(1 row) + +SELECT tables_colocated('d2', 'd3'); + tables_colocated +--------------------------------------------------------------------- + f +(1 row) + +SELECT tables_colocated('d1', 'd3'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +SELECT tables_colocated('d1', 'd4'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +-- break colocation of d2 +-- update colocation should not error if d2 doesn't have any colocated table. +SELECT update_distributed_table_colocation('d2', colocate_with => 'none'); + update_distributed_table_colocation +--------------------------------------------------------------------- + +(1 row) + +-- d1 and d3 and d4 should be colocated, d2 should have a new colocation id. +SELECT get_table_colocation_id('d1'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT get_table_colocation_id('d2'); + get_table_colocation_id +--------------------------------------------------------------------- + 12 +(1 row) + +SELECT get_table_colocation_id('d3'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT get_table_colocation_id('d4'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT tables_colocated('d1', 'd2'); + tables_colocated +--------------------------------------------------------------------- + f +(1 row) + +SELECT tables_colocated('d2', 'd3'); + tables_colocated +--------------------------------------------------------------------- + f +(1 row) + +SELECT tables_colocated('d1', 'd3'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +SELECT tables_colocated('d1', 'd4'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +SELECT update_distributed_table_colocation('d3', colocate_with => 'd2'); + update_distributed_table_colocation +--------------------------------------------------------------------- + +(1 row) + +-- d1 and d4 should be colocated, d2 and d3 should be colocated. +SELECT get_table_colocation_id('d1'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT get_table_colocation_id('d2'); + get_table_colocation_id +--------------------------------------------------------------------- + 12 +(1 row) + +SELECT get_table_colocation_id('d3'); + get_table_colocation_id +--------------------------------------------------------------------- + 12 +(1 row) + +SELECT get_table_colocation_id('d4'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT tables_colocated('d1', 'd2'); + tables_colocated +--------------------------------------------------------------------- + f +(1 row) + +SELECT tables_colocated('d2', 'd3'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +SELECT tables_colocated('d1', 'd3'); + tables_colocated +--------------------------------------------------------------------- + f +(1 row) + +SELECT tables_colocated('d1', 'd4'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +-- special case, colocate with a table named "none". +SELECT update_distributed_table_colocation('d3', colocate_with => '"none"'); + update_distributed_table_colocation +--------------------------------------------------------------------- + +(1 row) + +-- d1, d4, d3 and "none" should be colocated; +SELECT get_table_colocation_id('d1'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT get_table_colocation_id('d2'); + get_table_colocation_id +--------------------------------------------------------------------- + 12 +(1 row) + +SELECT get_table_colocation_id('d3'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT get_table_colocation_id('d4'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT get_table_colocation_id('none'); + get_table_colocation_id +--------------------------------------------------------------------- + 8 +(1 row) + +SELECT tables_colocated('d1', 'd2'); + tables_colocated +--------------------------------------------------------------------- + f +(1 row) + +SELECT tables_colocated('d2', 'd3'); + tables_colocated +--------------------------------------------------------------------- + f +(1 row) + +SELECT tables_colocated('d1', 'd3'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +SELECT tables_colocated('d1', 'd4'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +SELECT tables_colocated('d1', 'none'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +SELECT tables_colocated('d4', 'none'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +SELECT tables_colocated('d3', 'none'); + tables_colocated +--------------------------------------------------------------------- + t +(1 row) + +SELECT tables_colocated('d2', 'none'); + tables_colocated +--------------------------------------------------------------------- + f +(1 row) + +-- make sure reference and local tables return an error. +SELECT update_distributed_table_colocation('ref', colocate_with => 'none'); +ERROR: relation ref should be a hash distributed table +SELECT update_distributed_table_colocation('local_table', colocate_with => 'none'); +ERROR: relation local_table is not distributed +-- make sure that different types cannot be colocated +SELECT update_distributed_table_colocation('different_d1', colocate_with => 'd1'); +ERROR: cannot colocate tables d1 and different_d1 +DETAIL: Distribution column types don't match for d1 and different_d1. +SELECT update_distributed_table_colocation('d1', colocate_with => 'different_d1'); +ERROR: cannot colocate tables different_d1 and d1 +DETAIL: Distribution column types don't match for different_d1 and d1. +-- make sure that append distributed tables cannot be colocated +SELECT update_distributed_table_colocation('append_table', colocate_with => 'd1'); +ERROR: relation append_table should be a hash distributed table +SELECT update_distributed_table_colocation('d1', colocate_with => 'append_table'); +ERROR: relation append_table should be a hash distributed table +SELECT update_distributed_table_colocation('range_table', colocate_with => 'd1'); +ERROR: relation range_table should be a hash distributed table +SELECT update_distributed_table_colocation('d1', colocate_with => 'range_table'); +ERROR: relation range_table should be a hash distributed table -- drop tables to clean test space DROP TABLE table1_groupb; DROP TABLE table2_groupb; @@ -1055,3 +1456,13 @@ DROP TABLE table1_group_none_3; DROP TABLE table1_group_none; DROP TABLE table2_group_none; DROP TABLE table1_group_default; +DROP TABLE d1; +DROP TABLE d2; +DROP TABLE d3; +DROP TABLE d4; +DROP TABLE different_d1; +DROP TABLE append_table; +DROP TABLE range_table; +DROP TABLE none; +DROP TABLE ref; +DROP TABLE local_table; diff --git a/src/test/regress/sql/multi_colocation_utils.sql b/src/test/regress/sql/multi_colocation_utils.sql index 501546373..e81de3d39 100644 --- a/src/test/regress/sql/multi_colocation_utils.sql +++ b/src/test/regress/sql/multi_colocation_utils.sql @@ -92,6 +92,7 @@ SELECT master_create_distributed_table('table6_append', 'id', 'append'); SELECT master_create_empty_shard('table6_append'); SELECT master_create_empty_shard('table6_append'); + -- make table1_group1 and table2_group1 co-located manually SELECT colocation_test_colocate_tables('table1_group1', 'table2_group1'); @@ -430,6 +431,122 @@ SELECT create_distributed_table('table2_groupG', 'id', colocate_with => 'NONE'); SELECT mark_tables_colocated('table1_groupG', ARRAY['table2_groupG']); +CREATE TABLE d1(a int, b int); +CREATE TABLE d2(a int, b int); +CREATE TABLE d3(a int, b int); +CREATE TABLE d4(a int, b int); +CREATE TABLE different_d1(ch char); +CREATE TABLE append_table(a int, b int); +CREATE TABLE range_table(a int, b int); +-- special keyword none +CREATE TABLE none(a int, b int); +CREATE TABLE ref(a int); +CREATE TABLE local_table(a int); + +SELECT create_distributed_table('d1', 'a'); +SELECT create_distributed_table('d2', 'a', colocate_with => 'd1'); +SELECT create_distributed_table('d3', 'a', colocate_with => 'd2'); +SELECT create_distributed_table('d4', 'a', colocate_with => 'd3'); +SELECT create_distributed_table('none', 'a', colocate_with => 'd4'); +SELECT create_distributed_table('different_d1', 'ch'); +SELECT create_distributed_table('append_table', 'a', 'append'); +SELECT create_distributed_table('range_table', 'a', 'range'); + + +SELECT create_reference_table('ref'); + + +-- check d1, d2, d3 and d4 has the same colocation id => they are colocated. +SELECT get_table_colocation_id('d1'); +SELECT get_table_colocation_id('d2'); +SELECT get_table_colocation_id('d3'); +SELECT get_table_colocation_id('d4'); + + +SELECT tables_colocated('d1', 'd2'); +SELECT tables_colocated('d2', 'd3'); +SELECT tables_colocated('d2', 'd4'); +SELECT tables_colocated('d3', 'd4'); +SELECT tables_colocated('d1', 'd3'); +SELECT tables_colocated('d1', 'd4'); + +-- break colocation of d2 +SELECT update_distributed_table_colocation('d2', colocate_with => 'none'); + +-- d1 and d3 and d4 should be colocated, d2 should have a new colocation id. +SELECT get_table_colocation_id('d1'); +SELECT get_table_colocation_id('d2'); +SELECT get_table_colocation_id('d3'); +SELECT get_table_colocation_id('d4'); + +SELECT tables_colocated('d1', 'd2'); +SELECT tables_colocated('d2', 'd3'); +SELECT tables_colocated('d1', 'd3'); +SELECT tables_colocated('d1', 'd4'); + +-- break colocation of d2 +-- update colocation should not error if d2 doesn't have any colocated table. +SELECT update_distributed_table_colocation('d2', colocate_with => 'none'); + +-- d1 and d3 and d4 should be colocated, d2 should have a new colocation id. +SELECT get_table_colocation_id('d1'); +SELECT get_table_colocation_id('d2'); +SELECT get_table_colocation_id('d3'); +SELECT get_table_colocation_id('d4'); + +SELECT tables_colocated('d1', 'd2'); +SELECT tables_colocated('d2', 'd3'); +SELECT tables_colocated('d1', 'd3'); +SELECT tables_colocated('d1', 'd4'); + +SELECT update_distributed_table_colocation('d3', colocate_with => 'd2'); + +-- d1 and d4 should be colocated, d2 and d3 should be colocated. +SELECT get_table_colocation_id('d1'); +SELECT get_table_colocation_id('d2'); +SELECT get_table_colocation_id('d3'); +SELECT get_table_colocation_id('d4'); + +SELECT tables_colocated('d1', 'd2'); +SELECT tables_colocated('d2', 'd3'); +SELECT tables_colocated('d1', 'd3'); +SELECT tables_colocated('d1', 'd4'); + +-- special case, colocate with a table named "none". +SELECT update_distributed_table_colocation('d3', colocate_with => '"none"'); +-- d1, d4, d3 and "none" should be colocated; +SELECT get_table_colocation_id('d1'); +SELECT get_table_colocation_id('d2'); +SELECT get_table_colocation_id('d3'); +SELECT get_table_colocation_id('d4'); +SELECT get_table_colocation_id('none'); + +SELECT tables_colocated('d1', 'd2'); +SELECT tables_colocated('d2', 'd3'); +SELECT tables_colocated('d1', 'd3'); +SELECT tables_colocated('d1', 'd4'); +SELECT tables_colocated('d1', 'none'); +SELECT tables_colocated('d4', 'none'); +SELECT tables_colocated('d3', 'none'); +SELECT tables_colocated('d2', 'none'); + +-- make sure reference and local tables return an error. +SELECT update_distributed_table_colocation('ref', colocate_with => 'none'); +SELECT update_distributed_table_colocation('local_table', colocate_with => 'none'); + +-- make sure that different types cannot be colocated +SELECT update_distributed_table_colocation('different_d1', colocate_with => 'd1'); +SELECT update_distributed_table_colocation('d1', colocate_with => 'different_d1'); + +-- make sure that append distributed tables cannot be colocated +SELECT update_distributed_table_colocation('append_table', colocate_with => 'd1'); +SELECT update_distributed_table_colocation('d1', colocate_with => 'append_table'); +SELECT update_distributed_table_colocation('range_table', colocate_with => 'd1'); +SELECT update_distributed_table_colocation('d1', colocate_with => 'range_table'); + + + + -- drop tables to clean test space DROP TABLE table1_groupb; DROP TABLE table2_groupb; @@ -453,3 +570,15 @@ DROP TABLE table1_group_none_3; DROP TABLE table1_group_none; DROP TABLE table2_group_none; DROP TABLE table1_group_default; +DROP TABLE d1; +DROP TABLE d2; +DROP TABLE d3; +DROP TABLE d4; +DROP TABLE different_d1; +DROP TABLE append_table; +DROP TABLE range_table; +DROP TABLE none; +DROP TABLE ref; +DROP TABLE local_table; + +