Enable reference/distributed table creation from citus local tables

pull/4489/head
Onur Tirtir 2021-01-11 04:52:14 +03:00
parent 7180ef5df1
commit ccbc3de535
5 changed files with 699 additions and 8 deletions

View File

@ -29,6 +29,7 @@
#include "fmgr.h" #include "fmgr.h"
#include "access/hash.h" #include "access/hash.h"
#include "access/xact.h"
#include "catalog/dependency.h" #include "catalog/dependency.h"
#include "catalog/pg_am.h" #include "catalog/pg_am.h"
#include "columnar/cstore.h" #include "columnar/cstore.h"

View File

@ -114,6 +114,9 @@ static void EnsureLocalTableEmptyIfNecessary(Oid relationId, char distributionMe
static bool ShouldLocalTableBeEmpty(Oid relationId, char distributionMethod, bool static bool ShouldLocalTableBeEmpty(Oid relationId, char distributionMethod, bool
viaDeprecatedAPI); viaDeprecatedAPI);
static void EnsureCitusTableCanBeCreated(Oid relationOid); static void EnsureCitusTableCanBeCreated(Oid relationOid);
static List * GetFKeyCreationCommandsRelationInvolved(Oid relationId);
static Oid DropFKeysAndUndistributeTable(Oid relationId);
static void DropFKeysRelationInvolved(Oid relationId);
static bool LocalTableEmpty(Oid tableId); static bool LocalTableEmpty(Oid tableId);
static void CopyLocalDataIntoShards(Oid relationId); static void CopyLocalDataIntoShards(Oid relationId);
static List * TupleDescColumnNameList(TupleDesc tupleDescriptor); static List * TupleDescColumnNameList(TupleDesc tupleDescriptor);
@ -209,13 +212,14 @@ create_distributed_table(PG_FUNCTION_ARGS)
* backends manipulating this relation. * backends manipulating this relation.
*/ */
Relation relation = try_relation_open(relationId, ExclusiveLock); Relation relation = try_relation_open(relationId, ExclusiveLock);
if (relation == NULL) if (relation == NULL)
{ {
ereport(ERROR, (errmsg("could not create distributed table: " ereport(ERROR, (errmsg("could not create distributed table: "
"relation does not exist"))); "relation does not exist")));
} }
relation_close(relation, NoLock);
char *distributionColumnName = text_to_cstring(distributionColumnText); char *distributionColumnName = text_to_cstring(distributionColumnText);
Var *distributionColumn = BuildDistributionKeyFromColumnName(relation, Var *distributionColumn = BuildDistributionKeyFromColumnName(relation,
distributionColumnName); distributionColumnName);
@ -227,8 +231,6 @@ create_distributed_table(PG_FUNCTION_ARGS)
CreateDistributedTable(relationId, distributionColumn, distributionMethod, CreateDistributedTable(relationId, distributionColumn, distributionMethod,
ShardCount, colocateWithTableName, viaDeprecatedAPI); ShardCount, colocateWithTableName, viaDeprecatedAPI);
relation_close(relation, NoLock);
PG_RETURN_VOID(); PG_RETURN_VOID();
} }
@ -260,7 +262,14 @@ create_reference_table(PG_FUNCTION_ARGS)
* sense of this table until we've committed, and we don't want multiple * sense of this table until we've committed, and we don't want multiple
* backends manipulating this relation. * backends manipulating this relation.
*/ */
Relation relation = relation_open(relationId, ExclusiveLock); Relation relation = try_relation_open(relationId, ExclusiveLock);
if (relation == NULL)
{
ereport(ERROR, (errmsg("could not create reference table: "
"relation does not exist")));
}
relation_close(relation, NoLock);
List *workerNodeList = ActivePrimaryNodeList(ShareLock); List *workerNodeList = ActivePrimaryNodeList(ShareLock);
int workerCount = list_length(workerNodeList); int workerCount = list_length(workerNodeList);
@ -277,9 +286,6 @@ create_reference_table(PG_FUNCTION_ARGS)
CreateDistributedTable(relationId, distributionColumn, DISTRIBUTE_BY_NONE, CreateDistributedTable(relationId, distributionColumn, DISTRIBUTE_BY_NONE,
ShardCount, colocateWithTableName, viaDeprecatedAPI); ShardCount, colocateWithTableName, viaDeprecatedAPI);
relation_close(relation, NoLock);
PG_RETURN_VOID(); PG_RETURN_VOID();
} }
@ -338,6 +344,25 @@ void
CreateDistributedTable(Oid relationId, Var *distributionColumn, char distributionMethod, CreateDistributedTable(Oid relationId, Var *distributionColumn, char distributionMethod,
int shardCount, char *colocateWithTableName, bool viaDeprecatedAPI) int shardCount, char *colocateWithTableName, bool viaDeprecatedAPI)
{ {
/*
* EnsureTableNotDistributed errors out when relation is a citus table but
* we don't want to ask user to first undistribute their citus local tables
* when creating reference or distributed tables from them.
* For this reason, here we undistribute citus local tables beforehand.
* But since UndistributeTable does not support undistributing relations
* involved in foreign key relationships, we first drop foreign keys that
* given relation is involved, then we undistribute the relation and finally
* we re-create dropped foreign keys at the end of this function.
*/
List *fKeyCreationCommandsRelationInvolved = NIL;
if (IsCitusTableType(relationId, CITUS_LOCAL_TABLE))
{
/* store foreign key creation commands that relation is involved */
fKeyCreationCommandsRelationInvolved =
GetFKeyCreationCommandsRelationInvolved(relationId);
relationId = DropFKeysAndUndistributeTable(relationId);
}
/* /*
* distributed tables might have dependencies on different objects, since we create * distributed tables might have dependencies on different objects, since we create
* shards for a distributed table via multiple sessions these objects will be created * shards for a distributed table via multiple sessions these objects will be created
@ -440,6 +465,85 @@ CreateDistributedTable(Oid relationId, Var *distributionColumn, char distributio
CopyLocalDataIntoShards(relationId); CopyLocalDataIntoShards(relationId);
} }
} }
/* now recreate foreign keys that we dropped beforehand */
ExecuteAndLogDDLCommandList(fKeyCreationCommandsRelationInvolved);
}
/*
* GetFKeyCreationCommandsRelationInvolved returns a list of DDL commands to
* recreate the foreign keys that relation with relationId is involved.
*/
static List *
GetFKeyCreationCommandsRelationInvolved(Oid relationId)
{
int referencingFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS |
INCLUDE_ALL_TABLE_TYPES;
List *referencingFKeyCreationCommands =
GetForeignConstraintCommandsInternal(relationId, referencingFKeysFlag);
/* already captured self referencing foreign keys, so use EXCLUDE_SELF_REFERENCES */
int referencedFKeysFlag = INCLUDE_REFERENCED_CONSTRAINTS |
EXCLUDE_SELF_REFERENCES |
INCLUDE_ALL_TABLE_TYPES;
List *referencedFKeyCreationCommands =
GetForeignConstraintCommandsInternal(relationId, referencedFKeysFlag);
return list_concat(referencingFKeyCreationCommands, referencedFKeyCreationCommands);
}
/*
* DropFKeysAndUndistributeTable drops all foreign keys that relation with
* relationId is involved then undistributes it.
* Note that as UndistributeTable changes relationId of relation, this
* function also returns new relationId of relation.
* Also note that callers are responsible for storing & recreating foreign
* keys to be dropped if needed.
*/
static Oid
DropFKeysAndUndistributeTable(Oid relationId)
{
DropFKeysRelationInvolved(relationId);
/* store them before calling UndistributeTable as it changes relationId */
char *relationName = get_rel_name(relationId);
Oid schemaId = get_rel_namespace(relationId);
TableConversionParameters params = {
.relationId = relationId,
.cascadeViaForeignKeys = false
};
UndistributeTable(&params);
Oid newRelationId = get_relname_relid(relationName, schemaId);
/*
* We don't expect this to happen but to be on the safe side let's error
* out here.
*/
EnsureRelationExists(newRelationId);
return newRelationId;
}
/*
* DropFKeysRelationInvolved drops all foreign keys that relation with
* relationId is involved.
*/
static void
DropFKeysRelationInvolved(Oid relationId)
{
int referencingFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS |
INCLUDE_ALL_TABLE_TYPES;
DropRelationForeignKeys(relationId, referencingFKeysFlag);
/* already captured self referencing foreign keys, so use EXCLUDE_SELF_REFERENCES */
int referencedFKeysFlag = INCLUDE_REFERENCED_CONSTRAINTS |
EXCLUDE_SELF_REFERENCES |
INCLUDE_ALL_TABLE_TYPES;
DropRelationForeignKeys(relationId, referencedFKeysFlag);
} }

View File

@ -0,0 +1,369 @@
\set VERBOSITY terse
SET citus.next_shard_id TO 1800000;
SET citus.shard_replication_factor TO 1;
CREATE SCHEMA create_ref_dist_from_citus_local;
SET search_path TO create_ref_dist_from_citus_local;
SET client_min_messages to ERROR;
-- ensure that coordinator is added to pg_dist_node
SELECT 1 FROM master_add_node('localhost', :master_port, groupId => 0);
?column?
---------------------------------------------------------------------
1
(1 row)
CREATE TABLE citus_local_table_1 (col_1 INT UNIQUE);
CREATE TABLE citus_local_table_2 (col_1 INT UNIQUE);
CREATE TABLE citus_local_table_3 (col_1 INT UNIQUE);
CREATE TABLE citus_local_table_4 (col_1 INT UNIQUE);
ALTER TABLE citus_local_table_2 ADD CONSTRAINT fkey_1 FOREIGN KEY (col_1) REFERENCES citus_local_table_1(col_1);
ALTER TABLE citus_local_table_3 ADD CONSTRAINT fkey_2 FOREIGN KEY (col_1) REFERENCES citus_local_table_1(col_1);
ALTER TABLE citus_local_table_1 ADD CONSTRAINT fkey_3 FOREIGN KEY (col_1) REFERENCES citus_local_table_3(col_1);
ALTER TABLE citus_local_table_1 ADD CONSTRAINT fkey_4 FOREIGN KEY (col_1) REFERENCES citus_local_table_4(col_1);
ALTER TABLE citus_local_table_4 ADD CONSTRAINT fkey_5 FOREIGN KEY (col_1) REFERENCES citus_local_table_3(col_1);
ALTER TABLE citus_local_table_4 ADD CONSTRAINT fkey_6 FOREIGN KEY (col_1) REFERENCES citus_local_table_4(col_1);
SELECT create_citus_local_table('citus_local_table_1', cascade_via_foreign_keys=>true);
create_citus_local_table
---------------------------------------------------------------------
(1 row)
CREATE TABLE reference_table_1(col_1 INT UNIQUE, col_2 INT UNIQUE);
CREATE TABLE reference_table_2(col_1 INT UNIQUE, col_2 INT UNIQUE);
SELECT create_reference_table('reference_table_1');
create_reference_table
---------------------------------------------------------------------
(1 row)
SELECT create_reference_table('reference_table_2');
create_reference_table
---------------------------------------------------------------------
(1 row)
ALTER TABLE citus_local_table_4 ADD CONSTRAINT fkey_7 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_1);
ALTER TABLE reference_table_2 ADD CONSTRAINT fkey_8 FOREIGN KEY (col_1) REFERENCES citus_local_table_2(col_1);
CREATE TABLE distributed_table_1(col_1 INT UNIQUE, col_2 INT);
CREATE TABLE partitioned_dist_table_1 (col_1 INT UNIQUE, col_2 INT) PARTITION BY RANGE (col_1);
SELECT create_distributed_table('distributed_table_1', 'col_1');
create_distributed_table
---------------------------------------------------------------------
(1 row)
SELECT create_distributed_table('partitioned_dist_table_1', 'col_1');
create_distributed_table
---------------------------------------------------------------------
(1 row)
ALTER TABLE partitioned_dist_table_1 ADD CONSTRAINT fkey_9 FOREIGN KEY (col_1) REFERENCES distributed_table_1(col_1);
ALTER TABLE distributed_table_1 ADD CONSTRAINT fkey_10 FOREIGN KEY (col_1) REFERENCES reference_table_2(col_2);
ALTER TABLE partitioned_dist_table_1 ADD CONSTRAINT fkey_11 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_2);
-- As we will heavily rely on this feature after implementing automatic
-- convertion of postgres tables to citus local tables, let's have a
-- complex foreign key graph to see everything is fine.
--
-- distributed_table_1 <---------------- partitioned_dist_table_1
-- | |
-- v v
-- reference_table_2 _ reference_table_1
-- | | | ^
-- v | v |
-- citus_local_table_2 -> citus_local_table_1 -> citus_local_table_4
-- ^ | |
-- | v |
-- citus_local_table_3 <--------
-- Now print metadata after each of create_reference/distributed_table
-- operations to show that everything is fine. Also show that we
-- preserve foreign keys.
BEGIN;
SELECT create_reference_table('citus_local_table_1');
create_reference_table
---------------------------------------------------------------------
(1 row)
SELECT logicalrelid::text AS tablename, partmethod, repmodel FROM pg_dist_partition
WHERE logicalrelid::text IN (SELECT tablename FROM pg_tables WHERE schemaname='create_ref_dist_from_citus_local')
ORDER BY tablename;
tablename | partmethod | repmodel
---------------------------------------------------------------------
citus_local_table_1 | n | t
citus_local_table_2 | n | c
citus_local_table_3 | n | c
citus_local_table_4 | n | c
distributed_table_1 | h | c
partitioned_dist_table_1 | h | c
reference_table_1 | n | t
reference_table_2 | n | t
(8 rows)
SELECT COUNT(*)=11 FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_ref_dist_from_citus_local') AND
conname ~ '^fkey\_\d+$';
?column?
---------------------------------------------------------------------
t
(1 row)
ROLLBACK;
BEGIN;
SELECT create_reference_table('citus_local_table_2');
create_reference_table
---------------------------------------------------------------------
(1 row)
SELECT logicalrelid::text AS tablename, partmethod, repmodel FROM pg_dist_partition
WHERE logicalrelid::text IN (SELECT tablename FROM pg_tables WHERE schemaname='create_ref_dist_from_citus_local')
ORDER BY tablename;
tablename | partmethod | repmodel
---------------------------------------------------------------------
citus_local_table_1 | n | c
citus_local_table_2 | n | t
citus_local_table_3 | n | c
citus_local_table_4 | n | c
distributed_table_1 | h | c
partitioned_dist_table_1 | h | c
reference_table_1 | n | t
reference_table_2 | n | t
(8 rows)
SELECT COUNT(*)=11 FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_ref_dist_from_citus_local') AND
conname ~ '^fkey\_\d+$';
?column?
---------------------------------------------------------------------
t
(1 row)
ROLLBACK;
-- those two errors out as they reference to citus local tables but
-- distributed tables cannot reference to postgres or citus local tables
SELECT create_distributed_table('citus_local_table_1', 'col_1');
ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table
SELECT create_distributed_table('citus_local_table_4', 'col_1');
ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table
BEGIN;
SELECT create_reference_table('citus_local_table_2');
create_reference_table
---------------------------------------------------------------------
(1 row)
-- this would error out
SELECT create_reference_table('citus_local_table_2');
ERROR: table "citus_local_table_2" is already distributed
ROLLBACK;
-- test with a standalone table
CREATE TABLE citus_local_table_5 (col_1 INT UNIQUE);
SELECT create_citus_local_table('citus_local_table_5');
create_citus_local_table
---------------------------------------------------------------------
(1 row)
BEGIN;
SELECT create_distributed_table('citus_local_table_5', 'col_1');
create_distributed_table
---------------------------------------------------------------------
(1 row)
-- this would error out
SELECT create_reference_table('citus_local_table_5');
ERROR: table "citus_local_table_5" is already distributed
ROLLBACK;
BEGIN;
SELECT create_reference_table('citus_local_table_5');
create_reference_table
---------------------------------------------------------------------
(1 row)
ROLLBACK;
BEGIN;
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES citus_local_table_5(col_1);
SELECT create_reference_table('citus_local_table_5');
create_reference_table
---------------------------------------------------------------------
(1 row)
ROLLBACK;
BEGIN;
SELECT create_distributed_table('citus_local_table_5', 'col_1');
create_distributed_table
---------------------------------------------------------------------
(1 row)
SELECT logicalrelid::text AS tablename, partmethod, repmodel FROM pg_dist_partition
WHERE logicalrelid::text IN (SELECT tablename FROM pg_tables WHERE schemaname='create_ref_dist_from_citus_local')
ORDER BY tablename;
tablename | partmethod | repmodel
---------------------------------------------------------------------
citus_local_table_1 | n | c
citus_local_table_2 | n | c
citus_local_table_3 | n | c
citus_local_table_4 | n | c
citus_local_table_5 | h | c
distributed_table_1 | h | c
partitioned_dist_table_1 | h | c
reference_table_1 | n | t
reference_table_2 | n | t
(9 rows)
SELECT COUNT(*)=11 FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_ref_dist_from_citus_local') AND
conname ~ '^fkey\_\d+$';
?column?
---------------------------------------------------------------------
t
(1 row)
ROLLBACK;
BEGIN;
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES citus_local_table_5(col_1);
SELECT create_distributed_table('citus_local_table_5', 'col_1');
create_distributed_table
---------------------------------------------------------------------
(1 row)
SELECT logicalrelid::text AS tablename, partmethod, repmodel FROM pg_dist_partition
WHERE logicalrelid::text IN (SELECT tablename FROM pg_tables WHERE schemaname='create_ref_dist_from_citus_local')
ORDER BY tablename;
tablename | partmethod | repmodel
---------------------------------------------------------------------
citus_local_table_1 | n | c
citus_local_table_2 | n | c
citus_local_table_3 | n | c
citus_local_table_4 | n | c
citus_local_table_5 | h | c
distributed_table_1 | h | c
partitioned_dist_table_1 | h | c
reference_table_1 | n | t
reference_table_2 | n | t
(9 rows)
SELECT COUNT(*)=12 FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_ref_dist_from_citus_local') AND
conname ~ '^fkey\_\d+$';
?column?
---------------------------------------------------------------------
t
(1 row)
ROLLBACK;
BEGIN;
-- define a self reference and a foreign key to reference table
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES citus_local_table_5(col_1);
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_13 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_1);
SELECT create_distributed_table('citus_local_table_5', 'col_1');
create_distributed_table
---------------------------------------------------------------------
(1 row)
SELECT logicalrelid::text AS tablename, partmethod, repmodel FROM pg_dist_partition
WHERE logicalrelid::text IN (SELECT tablename FROM pg_tables WHERE schemaname='create_ref_dist_from_citus_local')
ORDER BY tablename;
tablename | partmethod | repmodel
---------------------------------------------------------------------
citus_local_table_1 | n | c
citus_local_table_2 | n | c
citus_local_table_3 | n | c
citus_local_table_4 | n | c
citus_local_table_5 | h | c
distributed_table_1 | h | c
partitioned_dist_table_1 | h | c
reference_table_1 | n | t
reference_table_2 | n | t
(9 rows)
SELECT COUNT(*)=13 FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_ref_dist_from_citus_local') AND
conname ~ '^fkey\_\d+$';
?column?
---------------------------------------------------------------------
t
(1 row)
ROLLBACK;
CREATE TABLE citus_local_table_6 (col_1 INT UNIQUE);
SELECT create_citus_local_table('citus_local_table_6');
create_citus_local_table
---------------------------------------------------------------------
(1 row)
BEGIN;
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES citus_local_table_6(col_1);
-- errors out as foreign keys from distributed tables to citus
-- local tables are not supported
SELECT create_distributed_table('citus_local_table_5', 'col_1');
ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table
ROLLBACK;
BEGIN;
-- errors out as foreign keys from citus local tables to distributed
-- tables are not supported
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES citus_local_table_6(col_1);
SELECT create_distributed_table('citus_local_table_6', 'col_1');
ERROR: cannot create foreign key constraint since foreign keys from reference tables and citus local tables to distributed tables are not supported
ROLLBACK;
-- have some more tests with foreign keys between citus local
-- and reference tables
BEGIN;
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES citus_local_table_6(col_1);
SELECT create_reference_table('citus_local_table_5');
create_reference_table
---------------------------------------------------------------------
(1 row)
ROLLBACK;
BEGIN;
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES citus_local_table_6(col_1);
SELECT create_reference_table('citus_local_table_6');
create_reference_table
---------------------------------------------------------------------
(1 row)
ROLLBACK;
BEGIN;
CREATE FUNCTION update_value() RETURNS trigger AS $update_value$
BEGIN
NEW.value := value+1 ;
RETURN NEW;
END;
$update_value$ LANGUAGE plpgsql;
CREATE TRIGGER update_value_dist
AFTER INSERT ON citus_local_table_6
FOR EACH ROW EXECUTE FUNCTION update_value();
-- show that we error out as we don't supprt triggers on distributed tables
SELECT create_distributed_table('citus_local_table_6', 'col_1');
ERROR: cannot distribute relation "citus_local_table_6" because it has triggers
ROLLBACK;
-- make sure that creating append / range distributed tables is also ok
BEGIN;
SELECT create_distributed_table('citus_local_table_5', 'col_1', 'range');
create_distributed_table
---------------------------------------------------------------------
(1 row)
ROLLBACK;
BEGIN;
ALTER TABLE citus_local_table_5 DROP CONSTRAINT citus_local_table_5_col_1_key;
SELECT create_distributed_table('citus_local_table_5', 'col_1', 'append');
create_distributed_table
---------------------------------------------------------------------
(1 row)
ROLLBACK;
-- cleanup at exit
DROP SCHEMA create_ref_dist_from_citus_local CASCADE;

View File

@ -324,7 +324,7 @@ test: replicate_reference_tables_to_coordinator
test: coordinator_shouldhaveshards test: coordinator_shouldhaveshards
test: local_shard_utility_command_execution test: local_shard_utility_command_execution
test: citus_local_tables test: citus_local_tables
test: multi_row_router_insert mixed_relkind_tests test: multi_row_router_insert mixed_relkind_tests create_ref_dist_from_citus_local
test: undistribute_table_cascade test: undistribute_table_cascade
test: create_citus_local_table_cascade test: create_citus_local_table_cascade

View File

@ -0,0 +1,217 @@
\set VERBOSITY terse
SET citus.next_shard_id TO 1800000;
SET citus.shard_replication_factor TO 1;
CREATE SCHEMA create_ref_dist_from_citus_local;
SET search_path TO create_ref_dist_from_citus_local;
SET client_min_messages to ERROR;
-- ensure that coordinator is added to pg_dist_node
SELECT 1 FROM master_add_node('localhost', :master_port, groupId => 0);
CREATE TABLE citus_local_table_1 (col_1 INT UNIQUE);
CREATE TABLE citus_local_table_2 (col_1 INT UNIQUE);
CREATE TABLE citus_local_table_3 (col_1 INT UNIQUE);
CREATE TABLE citus_local_table_4 (col_1 INT UNIQUE);
ALTER TABLE citus_local_table_2 ADD CONSTRAINT fkey_1 FOREIGN KEY (col_1) REFERENCES citus_local_table_1(col_1);
ALTER TABLE citus_local_table_3 ADD CONSTRAINT fkey_2 FOREIGN KEY (col_1) REFERENCES citus_local_table_1(col_1);
ALTER TABLE citus_local_table_1 ADD CONSTRAINT fkey_3 FOREIGN KEY (col_1) REFERENCES citus_local_table_3(col_1);
ALTER TABLE citus_local_table_1 ADD CONSTRAINT fkey_4 FOREIGN KEY (col_1) REFERENCES citus_local_table_4(col_1);
ALTER TABLE citus_local_table_4 ADD CONSTRAINT fkey_5 FOREIGN KEY (col_1) REFERENCES citus_local_table_3(col_1);
ALTER TABLE citus_local_table_4 ADD CONSTRAINT fkey_6 FOREIGN KEY (col_1) REFERENCES citus_local_table_4(col_1);
SELECT create_citus_local_table('citus_local_table_1', cascade_via_foreign_keys=>true);
CREATE TABLE reference_table_1(col_1 INT UNIQUE, col_2 INT UNIQUE);
CREATE TABLE reference_table_2(col_1 INT UNIQUE, col_2 INT UNIQUE);
SELECT create_reference_table('reference_table_1');
SELECT create_reference_table('reference_table_2');
ALTER TABLE citus_local_table_4 ADD CONSTRAINT fkey_7 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_1);
ALTER TABLE reference_table_2 ADD CONSTRAINT fkey_8 FOREIGN KEY (col_1) REFERENCES citus_local_table_2(col_1);
CREATE TABLE distributed_table_1(col_1 INT UNIQUE, col_2 INT);
CREATE TABLE partitioned_dist_table_1 (col_1 INT UNIQUE, col_2 INT) PARTITION BY RANGE (col_1);
SELECT create_distributed_table('distributed_table_1', 'col_1');
SELECT create_distributed_table('partitioned_dist_table_1', 'col_1');
ALTER TABLE partitioned_dist_table_1 ADD CONSTRAINT fkey_9 FOREIGN KEY (col_1) REFERENCES distributed_table_1(col_1);
ALTER TABLE distributed_table_1 ADD CONSTRAINT fkey_10 FOREIGN KEY (col_1) REFERENCES reference_table_2(col_2);
ALTER TABLE partitioned_dist_table_1 ADD CONSTRAINT fkey_11 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_2);
-- As we will heavily rely on this feature after implementing automatic
-- convertion of postgres tables to citus local tables, let's have a
-- complex foreign key graph to see everything is fine.
--
-- distributed_table_1 <---------------- partitioned_dist_table_1
-- | |
-- v v
-- reference_table_2 _ reference_table_1
-- | | | ^
-- v | v |
-- citus_local_table_2 -> citus_local_table_1 -> citus_local_table_4
-- ^ | |
-- | v |
-- citus_local_table_3 <--------
-- Now print metadata after each of create_reference/distributed_table
-- operations to show that everything is fine. Also show that we
-- preserve foreign keys.
BEGIN;
SELECT create_reference_table('citus_local_table_1');
SELECT logicalrelid::text AS tablename, partmethod, repmodel FROM pg_dist_partition
WHERE logicalrelid::text IN (SELECT tablename FROM pg_tables WHERE schemaname='create_ref_dist_from_citus_local')
ORDER BY tablename;
SELECT COUNT(*)=11 FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_ref_dist_from_citus_local') AND
conname ~ '^fkey\_\d+$';
ROLLBACK;
BEGIN;
SELECT create_reference_table('citus_local_table_2');
SELECT logicalrelid::text AS tablename, partmethod, repmodel FROM pg_dist_partition
WHERE logicalrelid::text IN (SELECT tablename FROM pg_tables WHERE schemaname='create_ref_dist_from_citus_local')
ORDER BY tablename;
SELECT COUNT(*)=11 FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_ref_dist_from_citus_local') AND
conname ~ '^fkey\_\d+$';
ROLLBACK;
-- those two errors out as they reference to citus local tables but
-- distributed tables cannot reference to postgres or citus local tables
SELECT create_distributed_table('citus_local_table_1', 'col_1');
SELECT create_distributed_table('citus_local_table_4', 'col_1');
BEGIN;
SELECT create_reference_table('citus_local_table_2');
-- this would error out
SELECT create_reference_table('citus_local_table_2');
ROLLBACK;
-- test with a standalone table
CREATE TABLE citus_local_table_5 (col_1 INT UNIQUE);
SELECT create_citus_local_table('citus_local_table_5');
BEGIN;
SELECT create_distributed_table('citus_local_table_5', 'col_1');
-- this would error out
SELECT create_reference_table('citus_local_table_5');
ROLLBACK;
BEGIN;
SELECT create_reference_table('citus_local_table_5');
ROLLBACK;
BEGIN;
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES citus_local_table_5(col_1);
SELECT create_reference_table('citus_local_table_5');
ROLLBACK;
BEGIN;
SELECT create_distributed_table('citus_local_table_5', 'col_1');
SELECT logicalrelid::text AS tablename, partmethod, repmodel FROM pg_dist_partition
WHERE logicalrelid::text IN (SELECT tablename FROM pg_tables WHERE schemaname='create_ref_dist_from_citus_local')
ORDER BY tablename;
SELECT COUNT(*)=11 FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_ref_dist_from_citus_local') AND
conname ~ '^fkey\_\d+$';
ROLLBACK;
BEGIN;
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES citus_local_table_5(col_1);
SELECT create_distributed_table('citus_local_table_5', 'col_1');
SELECT logicalrelid::text AS tablename, partmethod, repmodel FROM pg_dist_partition
WHERE logicalrelid::text IN (SELECT tablename FROM pg_tables WHERE schemaname='create_ref_dist_from_citus_local')
ORDER BY tablename;
SELECT COUNT(*)=12 FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_ref_dist_from_citus_local') AND
conname ~ '^fkey\_\d+$';
ROLLBACK;
BEGIN;
-- define a self reference and a foreign key to reference table
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES citus_local_table_5(col_1);
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_13 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_1);
SELECT create_distributed_table('citus_local_table_5', 'col_1');
SELECT logicalrelid::text AS tablename, partmethod, repmodel FROM pg_dist_partition
WHERE logicalrelid::text IN (SELECT tablename FROM pg_tables WHERE schemaname='create_ref_dist_from_citus_local')
ORDER BY tablename;
SELECT COUNT(*)=13 FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_ref_dist_from_citus_local') AND
conname ~ '^fkey\_\d+$';
ROLLBACK;
CREATE TABLE citus_local_table_6 (col_1 INT UNIQUE);
SELECT create_citus_local_table('citus_local_table_6');
BEGIN;
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES citus_local_table_6(col_1);
-- errors out as foreign keys from distributed tables to citus
-- local tables are not supported
SELECT create_distributed_table('citus_local_table_5', 'col_1');
ROLLBACK;
BEGIN;
-- errors out as foreign keys from citus local tables to distributed
-- tables are not supported
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES citus_local_table_6(col_1);
SELECT create_distributed_table('citus_local_table_6', 'col_1');
ROLLBACK;
-- have some more tests with foreign keys between citus local
-- and reference tables
BEGIN;
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES citus_local_table_6(col_1);
SELECT create_reference_table('citus_local_table_5');
ROLLBACK;
BEGIN;
ALTER TABLE citus_local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES citus_local_table_6(col_1);
SELECT create_reference_table('citus_local_table_6');
ROLLBACK;
BEGIN;
CREATE FUNCTION update_value() RETURNS trigger AS $update_value$
BEGIN
NEW.value := value+1 ;
RETURN NEW;
END;
$update_value$ LANGUAGE plpgsql;
CREATE TRIGGER update_value_dist
AFTER INSERT ON citus_local_table_6
FOR EACH ROW EXECUTE PROCEDURE update_value();
-- show that we error out as we don't supprt triggers on distributed tables
SELECT create_distributed_table('citus_local_table_6', 'col_1');
ROLLBACK;
-- make sure that creating append / range distributed tables is also ok
BEGIN;
SELECT create_distributed_table('citus_local_table_5', 'col_1', 'range');
ROLLBACK;
BEGIN;
ALTER TABLE citus_local_table_5 DROP CONSTRAINT citus_local_table_5_col_1_key;
SELECT create_distributed_table('citus_local_table_5', 'col_1', 'append');
ROLLBACK;
-- cleanup at exit
DROP SCHEMA create_ref_dist_from_citus_local CASCADE;