Add cascade_via_foreign_keys option to create_citus_local_table (#4462)

pull/4431/head
Onur Tirtir 2021-01-08 15:13:26 +03:00 committed by GitHub
parent 9c851817f1
commit 5289785da4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 517 additions and 35 deletions

View File

@ -326,6 +326,11 @@ GetCascadeOperationFunction(CascadeOperationType cascadeOperationType)
return UndistributeTable; return UndistributeTable;
} }
case CREATE_CITUS_LOCAL_TABLE:
{
return CreateCitusLocalTable;
}
default: default:
{ {
/* /*

View File

@ -38,7 +38,6 @@
#include "utils/syscache.h" #include "utils/syscache.h"
static void CreateCitusLocalTable(Oid relationId);
static void ErrorIfUnsupportedCreateCitusLocalTable(Relation relation); static void ErrorIfUnsupportedCreateCitusLocalTable(Relation relation);
static void ErrorIfUnsupportedCitusLocalTableKind(Oid relationId); static void ErrorIfUnsupportedCitusLocalTableKind(Oid relationId);
static List * GetShellTableDDLEventsForCitusLocalTable(Oid relationId); static List * GetShellTableDDLEventsForCitusLocalTable(Oid relationId);
@ -80,8 +79,9 @@ create_citus_local_table(PG_FUNCTION_ARGS)
CheckCitusVersion(ERROR); CheckCitusVersion(ERROR);
Oid relationId = PG_GETARG_OID(0); Oid relationId = PG_GETARG_OID(0);
bool cascadeViaForeignKeys = PG_GETARG_BOOL(1);
CreateCitusLocalTable(relationId); CreateCitusLocalTable(relationId, cascadeViaForeignKeys);
PG_RETURN_VOID(); PG_RETURN_VOID();
} }
@ -98,8 +98,8 @@ create_citus_local_table(PG_FUNCTION_ARGS)
* Similar to reference tables, it has only 1 placement. In addition to that, that * Similar to reference tables, it has only 1 placement. In addition to that, that
* single placement is only allowed to be on the coordinator. * single placement is only allowed to be on the coordinator.
*/ */
static void void
CreateCitusLocalTable(Oid relationId) CreateCitusLocalTable(Oid relationId, bool cascadeViaForeignKeys)
{ {
/* /*
* These checks should be done before acquiring any locks on relation. * These checks should be done before acquiring any locks on relation.
@ -118,7 +118,8 @@ CreateCitusLocalTable(Oid relationId)
* we open the relation with try_relation_open instead of relation_open * we open the relation with try_relation_open instead of relation_open
* to give a nice error in case the table is dropped by another backend. * to give a nice error in case the table is dropped by another backend.
*/ */
Relation relation = try_relation_open(relationId, AccessExclusiveLock); LOCKMODE lockMode = AccessExclusiveLock;
Relation relation = try_relation_open(relationId, lockMode);
ErrorIfUnsupportedCreateCitusLocalTable(relation); ErrorIfUnsupportedCreateCitusLocalTable(relation);
@ -131,6 +132,36 @@ CreateCitusLocalTable(Oid relationId)
*/ */
relation_close(relation, NoLock); relation_close(relation, NoLock);
bool tableHasExternalForeignKeys = TableHasExternalForeignKeys(relationId);
if (tableHasExternalForeignKeys && cascadeViaForeignKeys)
{
CascadeOperationForConnectedRelations(relationId, lockMode,
CREATE_CITUS_LOCAL_TABLE);
/*
* We converted every foreign key connected table in our subgraph
* including itself to a citus local table, so return here.
*/
return;
}
else if (tableHasExternalForeignKeys)
{
/*
* We do not allow creating citus local table if the table is involved in a
* foreign key relationship with "any other table". Note that we allow self
* references.
*/
char *qualifiedRelationName = generate_qualified_relation_name(relationId);
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("relation %s is involved in a foreign key "
"relationship with another table", qualifiedRelationName),
errhint("Use cascade_via_foreign_keys option to convert "
"all the relations involved in a foreign key "
"relationship with %s to a citus local table by "
"executing SELECT create_citus_local_table($$%s$$, "
"cascade_via_foreign_keys=>true)",
qualifiedRelationName, qualifiedRelationName)));
}
ObjectAddress tableAddress = { 0 }; ObjectAddress tableAddress = { 0 };
ObjectAddressSet(tableAddress, RelationRelationId, relationId); ObjectAddressSet(tableAddress, RelationRelationId, relationId);
@ -217,13 +248,6 @@ ErrorIfUnsupportedCreateCitusLocalTable(Relation relation)
*/ */
ErrorIfRelationIsAKnownShard(relationId); ErrorIfRelationIsAKnownShard(relationId);
/*
* We do not allow creating citus local table if the table is involved in a
* foreign key relationship with "any other table". Note that we allow self
* references.
*/
ErrorIfTableHasExternalForeignKeys(relationId);
/* we do not support policies in citus community */ /* we do not support policies in citus community */
ErrorIfUnsupportedPolicy(relation); ErrorIfUnsupportedPolicy(relation);
} }

View File

@ -825,11 +825,11 @@ FindForeignKeyOidWithName(List *foreignKeyOids, const char *inputConstraintName)
/* /*
* ErrorIfTableHasExternalForeignKeys errors out if the relation with relationId * TableHasExternalForeignKeys returns true if the relation with relationId is
* is involved in a foreign key relationship other than the self-referencing ones. * involved in a foreign key relationship other than the self-referencing ones.
*/ */
void bool
ErrorIfTableHasExternalForeignKeys(Oid relationId) TableHasExternalForeignKeys(Oid relationId)
{ {
int flags = (INCLUDE_REFERENCING_CONSTRAINTS | EXCLUDE_SELF_REFERENCES | int flags = (INCLUDE_REFERENCING_CONSTRAINTS | EXCLUDE_SELF_REFERENCES |
INCLUDE_ALL_TABLE_TYPES); INCLUDE_ALL_TABLE_TYPES);
@ -844,16 +844,10 @@ ErrorIfTableHasExternalForeignKeys(Oid relationId)
if (list_length(foreignKeysWithOtherTables) == 0) if (list_length(foreignKeysWithOtherTables) == 0)
{ {
return; return false;
} }
const char *relationName = get_rel_name(relationId); return true;
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("relation \"%s\" is involved in a foreign key relationship "
"with another table", relationName),
errhint("Drop foreign keys with other tables and re-define them "
"with ALTER TABLE commands after the current operation "
"is done.")));
} }

View File

@ -7,5 +7,6 @@ DROP FUNCTION IF EXISTS pg_catalog.citus_total_relation_size(regclass);
#include "udfs/citus_tables/10.0-1.sql" #include "udfs/citus_tables/10.0-1.sql"
#include "udfs/citus_finish_pg_upgrade/10.0-1.sql" #include "udfs/citus_finish_pg_upgrade/10.0-1.sql"
#include "udfs/undistribute_table/10.0-1.sql" #include "udfs/undistribute_table/10.0-1.sql"
#include "udfs/create_citus_local_table/10.0-1.sql"
#include "../../columnar/sql/columnar--9.5-1--10.0-1.sql" #include "../../columnar/sql/columnar--9.5-1--10.0-1.sql"

View File

@ -8,7 +8,9 @@
DROP VIEW public.citus_tables; DROP VIEW public.citus_tables;
DROP FUNCTION pg_catalog.citus_total_relation_size(regclass,boolean); DROP FUNCTION pg_catalog.citus_total_relation_size(regclass,boolean);
DROP FUNCTION pg_catalog.undistribute_table(regclass,boolean); DROP FUNCTION pg_catalog.undistribute_table(regclass,boolean);
DROP FUNCTION pg_catalog.create_citus_local_table(regclass,boolean);
#include "../udfs/citus_total_relation_size/7.0-1.sql" #include "../udfs/citus_total_relation_size/7.0-1.sql"
#include "../udfs/upgrade_to_reference_table/8.0-1.sql" #include "../udfs/upgrade_to_reference_table/8.0-1.sql"
#include "../udfs/undistribute_table/9.5-1.sql" #include "../udfs/undistribute_table/9.5-1.sql"
#include "../udfs/create_citus_local_table/9.5-1.sql"

View File

@ -0,0 +1,7 @@
DROP FUNCTION pg_catalog.create_citus_local_table(regclass);
CREATE OR REPLACE FUNCTION pg_catalog.create_citus_local_table(table_name regclass, cascade_via_foreign_keys boolean default false)
RETURNS void
LANGUAGE C STRICT
AS 'MODULE_PATHNAME', $$create_citus_local_table$$;
COMMENT ON FUNCTION pg_catalog.create_citus_local_table(table_name regclass, cascade_via_foreign_keys boolean)
IS 'create a citus local table';

View File

@ -1,6 +1,7 @@
CREATE OR REPLACE FUNCTION pg_catalog.create_citus_local_table(table_name regclass) DROP FUNCTION pg_catalog.create_citus_local_table(regclass);
CREATE OR REPLACE FUNCTION pg_catalog.create_citus_local_table(table_name regclass, cascade_via_foreign_keys boolean default false)
RETURNS void RETURNS void
LANGUAGE C STRICT LANGUAGE C STRICT
AS 'MODULE_PATHNAME', $$create_citus_local_table$$; AS 'MODULE_PATHNAME', $$create_citus_local_table$$;
COMMENT ON FUNCTION pg_catalog.create_citus_local_table(table_name regclass) COMMENT ON FUNCTION pg_catalog.create_citus_local_table(table_name regclass, cascade_via_foreign_keys boolean)
IS 'create a citus local table'; IS 'create a citus local table';

View File

@ -171,7 +171,7 @@ extern bool ConstraintIsAForeignKey(char *inputConstaintName, Oid relationOid);
extern bool ConstraintWithNameIsOfType(char *inputConstaintName, Oid relationId, extern bool ConstraintWithNameIsOfType(char *inputConstaintName, Oid relationId,
char targetConstraintType); char targetConstraintType);
extern bool ConstraintWithIdIsOfType(Oid constraintId, char targetConstraintType); extern bool ConstraintWithIdIsOfType(Oid constraintId, char targetConstraintType);
extern void ErrorIfTableHasExternalForeignKeys(Oid relationId); extern bool TableHasExternalForeignKeys(Oid relationId);
extern List * GetForeignKeyOids(Oid relationId, int flags); extern List * GetForeignKeyOids(Oid relationId, int flags);
extern Oid GetReferencedTableId(Oid foreignKeyId); extern Oid GetReferencedTableId(Oid foreignKeyId);
@ -387,7 +387,7 @@ extern List * CitusLocalTableTriggerCommandDDLJob(Oid relationId, char *triggerN
const char *queryString); const char *queryString);
extern Oid GetTriggerFunctionId(Oid triggerId); extern Oid GetTriggerFunctionId(Oid triggerId);
/* cascade_citus_table_function.c */ /* cascade_table_operation_for_connected_relations.c */
/* /*
* Flags that can be passed to CascadeOperationForConnectedRelations to specify * Flags that can be passed to CascadeOperationForConnectedRelations to specify
@ -399,6 +399,9 @@ typedef enum CascadeOperationType
/* execute UndistributeTable on each relation */ /* execute UndistributeTable on each relation */
UNDISTRIBUTE_TABLE = 1 << 1, UNDISTRIBUTE_TABLE = 1 << 1,
/* execute CreateCitusLocalTable on each relation */
CREATE_CITUS_LOCAL_TABLE = 1 << 2,
} CascadeOperationType; } CascadeOperationType;
extern void CascadeOperationForConnectedRelations(Oid relationId, LOCKMODE relLockMode, extern void CascadeOperationForConnectedRelations(Oid relationId, LOCKMODE relLockMode,
@ -407,6 +410,9 @@ extern void CascadeOperationForConnectedRelations(Oid relationId, LOCKMODE relLo
extern void ExecuteAndLogDDLCommandList(List *ddlCommandList); extern void ExecuteAndLogDDLCommandList(List *ddlCommandList);
extern void ExecuteAndLogDDLCommand(const char *commandString); extern void ExecuteAndLogDDLCommand(const char *commandString);
/* create_citus_local_table.c */
extern void CreateCitusLocalTable(Oid relationId, bool cascadeViaForeignKeys);
extern bool ShouldPropagateSetCommand(VariableSetStmt *setStmt); extern bool ShouldPropagateSetCommand(VariableSetStmt *setStmt);
extern void PostprocessVariableSetStmt(VariableSetStmt *setStmt, const char *setCommand); extern void PostprocessVariableSetStmt(VariableSetStmt *setStmt, const char *setCommand);

View File

@ -349,9 +349,9 @@ CREATE TABLE local_table_3 (a int primary key, b int references local_table_3(a)
-- below two should fail as we do not allow foreign keys between -- below two should fail as we do not allow foreign keys between
-- postgres local tables and citus local tables -- postgres local tables and citus local tables
SELECT create_citus_local_table('local_table_1'); SELECT create_citus_local_table('local_table_1');
ERROR: relation "local_table_1" is involved in a foreign key relationship with another table ERROR: relation citus_local_tables_test_schema.local_table_1 is involved in a foreign key relationship with another table
SELECT create_citus_local_table('local_table_2'); SELECT create_citus_local_table('local_table_2');
ERROR: relation "local_table_2" is involved in a foreign key relationship with another table ERROR: relation citus_local_tables_test_schema.local_table_2 is involved in a foreign key relationship with another table
-- below should work as we allow initial self references in citus local tables -- below should work as we allow initial self references in citus local tables
SELECT create_citus_local_table('local_table_3'); SELECT create_citus_local_table('local_table_3');
create_citus_local_table create_citus_local_table

View File

@ -0,0 +1,224 @@
\set VERBOSITY terse
SET citus.next_shard_id TO 1516000;
SET citus.shard_replication_factor TO 1;
CREATE SCHEMA create_citus_local_table_cascade;
SET search_path TO create_citus_local_table_cascade;
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 local_table_1 (col_1 INT UNIQUE);
CREATE TABLE local_table_2 (col_1 INT UNIQUE);
CREATE TABLE local_table_3 (col_1 INT UNIQUE);
CREATE TABLE local_table_4 (col_1 INT UNIQUE);
-- _
-- | |
-- | v
-- local_table_2 -> local_table_1 -> local_table_4
-- ^ | |
-- | v |
-- local_table_3 <--------
ALTER TABLE local_table_2 ADD CONSTRAINT fkey_1 FOREIGN KEY (col_1) REFERENCES local_table_1(col_1);
ALTER TABLE local_table_3 ADD CONSTRAINT fkey_2 FOREIGN KEY (col_1) REFERENCES local_table_1(col_1);
ALTER TABLE local_table_1 ADD CONSTRAINT fkey_3 FOREIGN KEY (col_1) REFERENCES local_table_3(col_1);
ALTER TABLE local_table_1 ADD CONSTRAINT fkey_4 FOREIGN KEY (col_1) REFERENCES local_table_4(col_1);
ALTER TABLE local_table_4 ADD CONSTRAINT fkey_5 FOREIGN KEY (col_1) REFERENCES local_table_3(col_1);
ALTER TABLE local_table_4 ADD CONSTRAINT fkey_6 FOREIGN KEY (col_1) REFERENCES local_table_4(col_1);
-- show that all of below fails as we didn't provide cascade_via_foreign_keys=true
SELECT create_citus_local_table('local_table_1');
ERROR: relation create_citus_local_table_cascade.local_table_1 is involved in a foreign key relationship with another table
SELECT create_citus_local_table('local_table_4', cascade_via_foreign_keys=>false);
ERROR: relation create_citus_local_table_cascade.local_table_4 is involved in a foreign key relationship with another table
-- In each of below two transaction blocks, show that we preserve foreign keys.
-- Also show that we converted all local_table_xxx tables in current schema
-- to citus local tables after create_citus_local_table (cascade).
-- So in each transaction, both selects should return true.
BEGIN;
SELECT conname, conrelid::regclass::text, confrelid::regclass::text
FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_citus_local_table_cascade') AND
conname ~ '^fkey\_\d+$'
ORDER BY 1,2,3;
conname | conrelid | confrelid
---------------------------------------------------------------------
fkey_1 | local_table_2 | local_table_1
fkey_2 | local_table_3 | local_table_1
fkey_3 | local_table_1 | local_table_3
fkey_4 | local_table_1 | local_table_4
fkey_5 | local_table_4 | local_table_3
fkey_6 | local_table_4 | local_table_4
(6 rows)
SELECT create_citus_local_table('local_table_1', cascade_via_foreign_keys=>true);
create_citus_local_table
---------------------------------------------------------------------
(1 row)
-- show that we do parallel execution
show citus.multi_shard_modify_mode;
citus.multi_shard_modify_mode
---------------------------------------------------------------------
parallel
(1 row)
SELECT conname, conrelid::regclass::text, confrelid::regclass::text
FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_citus_local_table_cascade') AND
conname ~ '^fkey\_\d+$'
ORDER BY 1,2,3;
conname | conrelid | confrelid
---------------------------------------------------------------------
fkey_1 | local_table_2 | local_table_1
fkey_2 | local_table_3 | local_table_1
fkey_3 | local_table_1 | local_table_3
fkey_4 | local_table_1 | local_table_4
fkey_5 | local_table_4 | local_table_3
fkey_6 | local_table_4 | local_table_4
(6 rows)
SELECT COUNT(*)=4 FROM pg_dist_partition, pg_tables
WHERE tablename=logicalrelid::regclass::text AND
schemaname='create_citus_local_table_cascade';
?column?
---------------------------------------------------------------------
t
(1 row)
ROLLBACK;
BEGIN;
SELECT create_citus_local_table('local_table_4', cascade_via_foreign_keys=>true);
create_citus_local_table
---------------------------------------------------------------------
(1 row)
SELECT COUNT(*)=6 FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_citus_local_table_cascade') AND
conname ~ '^fkey\_\d+$';
?column?
---------------------------------------------------------------------
t
(1 row)
SELECT COUNT(*)=4 FROM pg_dist_partition, pg_tables
WHERE tablename=logicalrelid::regclass::text AND
schemaname='create_citus_local_table_cascade';
?column?
---------------------------------------------------------------------
t
(1 row)
ROLLBACK;
BEGIN;
CREATE TABLE partitioned_table (col_1 INT REFERENCES local_table_1 (col_1)) PARTITION BY RANGE (col_1);
-- now that we introduced a partitioned table into our foreign key subgraph,
-- create_citus_local_table(cascade_via_foreign_keys) would fail for
-- partitioned_table as create_citus_local_table doesn't support partitioned tables
SELECT create_citus_local_table('local_table_2', cascade_via_foreign_keys=>true);
ERROR: cannot create citus local table "partitioned_table", only regular tables and foreign tables are supported for citus local table creation
ROLLBACK;
BEGIN;
DROP TABLE local_table_2;
-- show that create_citus_local_table(cascade_via_foreign_keys) works fine after
-- dropping one of the relations from foreign key graph
SELECT create_citus_local_table('local_table_1', cascade_via_foreign_keys=>true);
create_citus_local_table
---------------------------------------------------------------------
(1 row)
ROLLBACK;
BEGIN;
-- split local_table_2 from foreign key subgraph
ALTER TABLE local_table_1 DROP CONSTRAINT local_table_1_col_1_key CASCADE;
-- now that local_table_2 does not have any foreign keys, cascade_via_foreign_keys=true
-- is not needed but show that it still works fine
SELECT create_citus_local_table('local_table_2', cascade_via_foreign_keys=>true);
create_citus_local_table
---------------------------------------------------------------------
(1 row)
-- show citus tables in current schema
SELECT tablename FROM pg_dist_partition, pg_tables
WHERE tablename=logicalrelid::regclass::text AND
schemaname='create_citus_local_table_cascade'
ORDER BY 1;
tablename
---------------------------------------------------------------------
local_table_2
(1 row)
ROLLBACK;
BEGIN;
-- split local_table_2 from foreign key subgraph
ALTER TABLE local_table_1 DROP CONSTRAINT local_table_1_col_1_key CASCADE;
-- add a self reference on local_table_2
ALTER TABLE local_table_2 ADD CONSTRAINT fkey_self FOREIGN KEY(col_1) REFERENCES local_table_2(col_1);
-- now that local_table_2 does not have any
-- foreign key relationships with other tables but a self
-- referencing foreign key, cascade_via_foreign_keys=true
-- is not needed but show that it still works fine
SELECT create_citus_local_table('local_table_2', cascade_via_foreign_keys=>true);
create_citus_local_table
---------------------------------------------------------------------
(1 row)
-- show citus tables in current schema
SELECT tablename FROM pg_dist_partition, pg_tables
WHERE tablename=logicalrelid::regclass::text AND
schemaname='create_citus_local_table_cascade'
ORDER BY 1;
tablename
---------------------------------------------------------------------
local_table_2
(1 row)
ROLLBACK;
CREATE TABLE distributed_table(col INT);
SELECT create_distributed_Table('distributed_table', 'col');
create_distributed_table
---------------------------------------------------------------------
(1 row)
BEGIN;
SELECT * FROM distributed_table;
col
---------------------------------------------------------------------
(0 rows)
-- succeeds as create_citus_local_table would also prefer parallel
-- execution like above select
SELECT create_citus_local_table('local_table_4', cascade_via_foreign_keys=>true);
create_citus_local_table
---------------------------------------------------------------------
(1 row)
ROLLBACK;
BEGIN;
set citus.multi_shard_modify_mode to 'sequential';
-- sequetial execution also works fine
SELECT create_citus_local_table('local_table_4', cascade_via_foreign_keys=>true);
create_citus_local_table
---------------------------------------------------------------------
(1 row)
ROLLBACK;
-- test behaviour when outside of transaction block
SELECT create_citus_local_table('local_table_4', cascade_via_foreign_keys=>true);
create_citus_local_table
---------------------------------------------------------------------
(1 row)
-- cleanup at exit
DROP SCHEMA create_citus_local_table_cascade CASCADE;

View File

@ -446,6 +446,7 @@ SELECT * FROM print_extension_changes();
previous_object | current_object previous_object | current_object
--------------------------------------------------------------------- ---------------------------------------------------------------------
function citus_total_relation_size(regclass) | function citus_total_relation_size(regclass) |
function create_citus_local_table(regclass) |
function undistribute_table(regclass) | function undistribute_table(regclass) |
function upgrade_to_reference_table(regclass) | function upgrade_to_reference_table(regclass) |
| access method columnar | access method columnar
@ -454,6 +455,7 @@ SELECT * FROM print_extension_changes();
| function citus_internal.columnar_ensure_objects_exist() | function citus_internal.columnar_ensure_objects_exist()
| function citus_total_relation_size(regclass,boolean) | function citus_total_relation_size(regclass,boolean)
| function columnar.columnar_handler(internal) | function columnar.columnar_handler(internal)
| function create_citus_local_table(regclass,boolean)
| function undistribute_table(regclass,boolean) | function undistribute_table(regclass,boolean)
| schema columnar | schema columnar
| sequence columnar.storageid_seq | sequence columnar.storageid_seq
@ -461,7 +463,7 @@ SELECT * FROM print_extension_changes();
| table columnar.columnar_stripes | table columnar.columnar_stripes
| table columnar.options | table columnar.options
| view citus_tables | view citus_tables
(16 rows) (18 rows)
DROP TABLE prev_objects, extension_diff; DROP TABLE prev_objects, extension_diff;
-- show running version -- show running version

View File

@ -446,10 +446,12 @@ SELECT * FROM print_extension_changes();
previous_object | current_object previous_object | current_object
--------------------------------------------------------------------- ---------------------------------------------------------------------
function citus_total_relation_size(regclass) | function citus_total_relation_size(regclass) |
function create_citus_local_table(regclass) |
function undistribute_table(regclass) | function undistribute_table(regclass) |
function upgrade_to_reference_table(regclass) | function upgrade_to_reference_table(regclass) |
| function citus_internal.columnar_ensure_objects_exist() | function citus_internal.columnar_ensure_objects_exist()
| function citus_total_relation_size(regclass,boolean) | function citus_total_relation_size(regclass,boolean)
| function create_citus_local_table(regclass,boolean)
| function undistribute_table(regclass,boolean) | function undistribute_table(regclass,boolean)
| schema columnar | schema columnar
| sequence columnar.storageid_seq | sequence columnar.storageid_seq
@ -457,7 +459,7 @@ SELECT * FROM print_extension_changes();
| table columnar.columnar_stripes | table columnar.columnar_stripes
| table columnar.options | table columnar.options
| view citus_tables | view citus_tables
(12 rows) (14 rows)
DROP TABLE prev_objects, extension_diff; DROP TABLE prev_objects, extension_diff;
-- show running version -- show running version

View File

@ -1085,6 +1085,18 @@ NOTICE: Renaming the new table to single_node.citus_local_table_1
(1 row) (1 row)
CREATE TABLE local_table_1 (col_1 INT UNIQUE);
CREATE TABLE local_table_2 (col_1 INT UNIQUE);
CREATE TABLE local_table_3 (col_1 INT UNIQUE);
ALTER TABLE local_table_2 ADD CONSTRAINT fkey_6 FOREIGN KEY (col_1) REFERENCES local_table_1(col_1);
ALTER TABLE local_table_3 ADD CONSTRAINT fkey_7 FOREIGN KEY (col_1) REFERENCES local_table_1(col_1);
ALTER TABLE local_table_1 ADD CONSTRAINT fkey_8 FOREIGN KEY (col_1) REFERENCES local_table_1(col_1);
SELECT create_citus_local_table('local_table_2', cascade_via_foreign_keys=>true);
create_citus_local_table
---------------------------------------------------------------------
(1 row)
CREATE PROCEDURE call_delegation(x int) LANGUAGE plpgsql AS $$ CREATE PROCEDURE call_delegation(x int) LANGUAGE plpgsql AS $$
BEGIN BEGIN
INSERT INTO test (x) VALUES ($1); INSERT INTO test (x) VALUES ($1);

View File

@ -46,6 +46,13 @@ SELECT create_citus_local_table('citus_local_table_1');
(1 row) (1 row)
CREATE TABLE citus_local_table_2 (col_1 INT UNIQUE);
SELECT create_citus_local_table('citus_local_table_2');
create_citus_local_table
---------------------------------------------------------------------
(1 row)
CREATE TABLE partitioned_table_1 (col_1 INT UNIQUE, col_2 INT) PARTITION BY RANGE (col_1); CREATE TABLE partitioned_table_1 (col_1 INT UNIQUE, col_2 INT) PARTITION BY RANGE (col_1);
CREATE TABLE partitioned_table_1_100_200 PARTITION OF partitioned_table_1 FOR VALUES FROM (100) TO (200); CREATE TABLE partitioned_table_1_100_200 PARTITION OF partitioned_table_1 FOR VALUES FROM (100) TO (200);
CREATE TABLE partitioned_table_1_200_300 PARTITION OF partitioned_table_1 FOR VALUES FROM (200) TO (300); CREATE TABLE partitioned_table_1_200_300 PARTITION OF partitioned_table_1 FOR VALUES FROM (200) TO (300);
@ -60,6 +67,7 @@ ALTER TABLE reference_table_1 ADD CONSTRAINT fkey_2 FOREIGN KEY (col_2) REFERENC
ALTER TABLE distributed_table_1 ADD CONSTRAINT fkey_3 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_1); ALTER TABLE distributed_table_1 ADD CONSTRAINT fkey_3 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_1);
ALTER TABLE citus_local_table_1 ADD CONSTRAINT fkey_4 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_2); ALTER TABLE citus_local_table_1 ADD CONSTRAINT fkey_4 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_2);
ALTER TABLE partitioned_table_1 ADD CONSTRAINT fkey_5 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_2); ALTER TABLE partitioned_table_1 ADD CONSTRAINT fkey_5 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_2);
ALTER TABLE citus_local_table_1 ADD CONSTRAINT fkey_6 FOREIGN KEY (col_1) REFERENCES citus_local_table_2(col_1);
SELECT undistribute_table('partitioned_table_1', cascade_via_foreign_keys=>true); SELECT undistribute_table('partitioned_table_1', cascade_via_foreign_keys=>true);
undistribute_table undistribute_table
--------------------------------------------------------------------- ---------------------------------------------------------------------
@ -77,5 +85,25 @@ $$);
(localhost,57638,t,0) (localhost,57638,t,0)
(2 rows) (2 rows)
-- drop parititoned table as create_citus_local_table doesn't support partitioned tables
DROP TABLE partitioned_table_1;
SELECT create_citus_local_table('citus_local_table_1', cascade_via_foreign_keys=>true);
create_citus_local_table
---------------------------------------------------------------------
(1 row)
-- both workers should print 4 as we converted all tables except
-- partitioned table in this schema to a citus local table
SELECT run_command_on_workers(
$$
SELECT count(*) FROM pg_catalog.pg_tables WHERE schemaname='undistribute_table_cascade_mx'
$$);
run_command_on_workers
---------------------------------------------------------------------
(localhost,57637,t,4)
(localhost,57638,t,4)
(2 rows)
-- cleanup at exit -- cleanup at exit
DROP SCHEMA undistribute_table_cascade_mx CASCADE; DROP SCHEMA undistribute_table_cascade_mx CASCADE;

View File

@ -76,7 +76,7 @@ ORDER BY 1;
function coord_combine_agg(oid,cstring,anyelement) function coord_combine_agg(oid,cstring,anyelement)
function coord_combine_agg_ffunc(internal,oid,cstring,anyelement) function coord_combine_agg_ffunc(internal,oid,cstring,anyelement)
function coord_combine_agg_sfunc(internal,oid,cstring,anyelement) function coord_combine_agg_sfunc(internal,oid,cstring,anyelement)
function create_citus_local_table(regclass) function create_citus_local_table(regclass,boolean)
function create_distributed_function(regprocedure,text,text) function create_distributed_function(regprocedure,text,text)
function create_distributed_table(regclass,text,citus.distribution_type,text) function create_distributed_table(regclass,text,citus.distribution_type,text)
function create_intermediate_result(text,text) function create_intermediate_result(text,text)

View File

@ -72,7 +72,7 @@ ORDER BY 1;
function coord_combine_agg(oid,cstring,anyelement) function coord_combine_agg(oid,cstring,anyelement)
function coord_combine_agg_ffunc(internal,oid,cstring,anyelement) function coord_combine_agg_ffunc(internal,oid,cstring,anyelement)
function coord_combine_agg_sfunc(internal,oid,cstring,anyelement) function coord_combine_agg_sfunc(internal,oid,cstring,anyelement)
function create_citus_local_table(regclass) function create_citus_local_table(regclass,boolean)
function create_distributed_function(regprocedure,text,text) function create_distributed_function(regprocedure,text,text)
function create_distributed_table(regclass,text,citus.distribution_type,text) function create_distributed_table(regclass,text,citus.distribution_type,text)
function create_intermediate_result(text,text) function create_intermediate_result(text,text)

View File

@ -323,7 +323,9 @@ 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 undistribute_table_cascade test: multi_row_router_insert mixed_relkind_tests
test: undistribute_table_cascade
test: create_citus_local_table_cascade
test: remove_coordinator test: remove_coordinator

View File

@ -0,0 +1,147 @@
\set VERBOSITY terse
SET citus.next_shard_id TO 1516000;
SET citus.shard_replication_factor TO 1;
CREATE SCHEMA create_citus_local_table_cascade;
SET search_path TO create_citus_local_table_cascade;
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 local_table_1 (col_1 INT UNIQUE);
CREATE TABLE local_table_2 (col_1 INT UNIQUE);
CREATE TABLE local_table_3 (col_1 INT UNIQUE);
CREATE TABLE local_table_4 (col_1 INT UNIQUE);
-- _
-- | |
-- | v
-- local_table_2 -> local_table_1 -> local_table_4
-- ^ | |
-- | v |
-- local_table_3 <--------
ALTER TABLE local_table_2 ADD CONSTRAINT fkey_1 FOREIGN KEY (col_1) REFERENCES local_table_1(col_1);
ALTER TABLE local_table_3 ADD CONSTRAINT fkey_2 FOREIGN KEY (col_1) REFERENCES local_table_1(col_1);
ALTER TABLE local_table_1 ADD CONSTRAINT fkey_3 FOREIGN KEY (col_1) REFERENCES local_table_3(col_1);
ALTER TABLE local_table_1 ADD CONSTRAINT fkey_4 FOREIGN KEY (col_1) REFERENCES local_table_4(col_1);
ALTER TABLE local_table_4 ADD CONSTRAINT fkey_5 FOREIGN KEY (col_1) REFERENCES local_table_3(col_1);
ALTER TABLE local_table_4 ADD CONSTRAINT fkey_6 FOREIGN KEY (col_1) REFERENCES local_table_4(col_1);
-- show that all of below fails as we didn't provide cascade_via_foreign_keys=true
SELECT create_citus_local_table('local_table_1');
SELECT create_citus_local_table('local_table_4', cascade_via_foreign_keys=>false);
-- In each of below two transaction blocks, show that we preserve foreign keys.
-- Also show that we converted all local_table_xxx tables in current schema
-- to citus local tables after create_citus_local_table (cascade).
-- So in each transaction, both selects should return true.
BEGIN;
SELECT conname, conrelid::regclass::text, confrelid::regclass::text
FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_citus_local_table_cascade') AND
conname ~ '^fkey\_\d+$'
ORDER BY 1,2,3;
SELECT create_citus_local_table('local_table_1', cascade_via_foreign_keys=>true);
-- show that we do parallel execution
show citus.multi_shard_modify_mode;
SELECT conname, conrelid::regclass::text, confrelid::regclass::text
FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_citus_local_table_cascade') AND
conname ~ '^fkey\_\d+$'
ORDER BY 1,2,3;
SELECT COUNT(*)=4 FROM pg_dist_partition, pg_tables
WHERE tablename=logicalrelid::regclass::text AND
schemaname='create_citus_local_table_cascade';
ROLLBACK;
BEGIN;
SELECT create_citus_local_table('local_table_4', cascade_via_foreign_keys=>true);
SELECT COUNT(*)=6 FROM pg_constraint
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname='create_citus_local_table_cascade') AND
conname ~ '^fkey\_\d+$';
SELECT COUNT(*)=4 FROM pg_dist_partition, pg_tables
WHERE tablename=logicalrelid::regclass::text AND
schemaname='create_citus_local_table_cascade';
ROLLBACK;
BEGIN;
CREATE TABLE partitioned_table (col_1 INT REFERENCES local_table_1 (col_1)) PARTITION BY RANGE (col_1);
-- now that we introduced a partitioned table into our foreign key subgraph,
-- create_citus_local_table(cascade_via_foreign_keys) would fail for
-- partitioned_table as create_citus_local_table doesn't support partitioned tables
SELECT create_citus_local_table('local_table_2', cascade_via_foreign_keys=>true);
ROLLBACK;
BEGIN;
DROP TABLE local_table_2;
-- show that create_citus_local_table(cascade_via_foreign_keys) works fine after
-- dropping one of the relations from foreign key graph
SELECT create_citus_local_table('local_table_1', cascade_via_foreign_keys=>true);
ROLLBACK;
BEGIN;
-- split local_table_2 from foreign key subgraph
ALTER TABLE local_table_1 DROP CONSTRAINT local_table_1_col_1_key CASCADE;
-- now that local_table_2 does not have any foreign keys, cascade_via_foreign_keys=true
-- is not needed but show that it still works fine
SELECT create_citus_local_table('local_table_2', cascade_via_foreign_keys=>true);
-- show citus tables in current schema
SELECT tablename FROM pg_dist_partition, pg_tables
WHERE tablename=logicalrelid::regclass::text AND
schemaname='create_citus_local_table_cascade'
ORDER BY 1;
ROLLBACK;
BEGIN;
-- split local_table_2 from foreign key subgraph
ALTER TABLE local_table_1 DROP CONSTRAINT local_table_1_col_1_key CASCADE;
-- add a self reference on local_table_2
ALTER TABLE local_table_2 ADD CONSTRAINT fkey_self FOREIGN KEY(col_1) REFERENCES local_table_2(col_1);
-- now that local_table_2 does not have any
-- foreign key relationships with other tables but a self
-- referencing foreign key, cascade_via_foreign_keys=true
-- is not needed but show that it still works fine
SELECT create_citus_local_table('local_table_2', cascade_via_foreign_keys=>true);
-- show citus tables in current schema
SELECT tablename FROM pg_dist_partition, pg_tables
WHERE tablename=logicalrelid::regclass::text AND
schemaname='create_citus_local_table_cascade'
ORDER BY 1;
ROLLBACK;
CREATE TABLE distributed_table(col INT);
SELECT create_distributed_Table('distributed_table', 'col');
BEGIN;
SELECT * FROM distributed_table;
-- succeeds as create_citus_local_table would also prefer parallel
-- execution like above select
SELECT create_citus_local_table('local_table_4', cascade_via_foreign_keys=>true);
ROLLBACK;
BEGIN;
set citus.multi_shard_modify_mode to 'sequential';
-- sequetial execution also works fine
SELECT create_citus_local_table('local_table_4', cascade_via_foreign_keys=>true);
ROLLBACK;
-- test behaviour when outside of transaction block
SELECT create_citus_local_table('local_table_4', cascade_via_foreign_keys=>true);
-- cleanup at exit
DROP SCHEMA create_citus_local_table_cascade CASCADE;

View File

@ -598,6 +598,16 @@ ALTER TABLE partitioned_table_1 ADD CONSTRAINT fkey_5 FOREIGN KEY (col_1) REFERE
SELECT undistribute_table('partitioned_table_1', cascade_via_foreign_keys=>true); SELECT undistribute_table('partitioned_table_1', cascade_via_foreign_keys=>true);
CREATE TABLE local_table_1 (col_1 INT UNIQUE);
CREATE TABLE local_table_2 (col_1 INT UNIQUE);
CREATE TABLE local_table_3 (col_1 INT UNIQUE);
ALTER TABLE local_table_2 ADD CONSTRAINT fkey_6 FOREIGN KEY (col_1) REFERENCES local_table_1(col_1);
ALTER TABLE local_table_3 ADD CONSTRAINT fkey_7 FOREIGN KEY (col_1) REFERENCES local_table_1(col_1);
ALTER TABLE local_table_1 ADD CONSTRAINT fkey_8 FOREIGN KEY (col_1) REFERENCES local_table_1(col_1);
SELECT create_citus_local_table('local_table_2', cascade_via_foreign_keys=>true);
CREATE PROCEDURE call_delegation(x int) LANGUAGE plpgsql AS $$ CREATE PROCEDURE call_delegation(x int) LANGUAGE plpgsql AS $$
BEGIN BEGIN
INSERT INTO test (x) VALUES ($1); INSERT INTO test (x) VALUES ($1);

View File

@ -25,6 +25,9 @@ SELECT create_distributed_table('distributed_table_1', 'col_1');
CREATE TABLE citus_local_table_1 (col_1 INT UNIQUE); CREATE TABLE citus_local_table_1 (col_1 INT UNIQUE);
SELECT create_citus_local_table('citus_local_table_1'); SELECT create_citus_local_table('citus_local_table_1');
CREATE TABLE citus_local_table_2 (col_1 INT UNIQUE);
SELECT create_citus_local_table('citus_local_table_2');
CREATE TABLE partitioned_table_1 (col_1 INT UNIQUE, col_2 INT) PARTITION BY RANGE (col_1); CREATE TABLE partitioned_table_1 (col_1 INT UNIQUE, col_2 INT) PARTITION BY RANGE (col_1);
CREATE TABLE partitioned_table_1_100_200 PARTITION OF partitioned_table_1 FOR VALUES FROM (100) TO (200); CREATE TABLE partitioned_table_1_100_200 PARTITION OF partitioned_table_1 FOR VALUES FROM (100) TO (200);
CREATE TABLE partitioned_table_1_200_300 PARTITION OF partitioned_table_1 FOR VALUES FROM (200) TO (300); CREATE TABLE partitioned_table_1_200_300 PARTITION OF partitioned_table_1 FOR VALUES FROM (200) TO (300);
@ -35,6 +38,7 @@ ALTER TABLE reference_table_1 ADD CONSTRAINT fkey_2 FOREIGN KEY (col_2) REFERENC
ALTER TABLE distributed_table_1 ADD CONSTRAINT fkey_3 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_1); ALTER TABLE distributed_table_1 ADD CONSTRAINT fkey_3 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_1);
ALTER TABLE citus_local_table_1 ADD CONSTRAINT fkey_4 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_2); ALTER TABLE citus_local_table_1 ADD CONSTRAINT fkey_4 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_2);
ALTER TABLE partitioned_table_1 ADD CONSTRAINT fkey_5 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_2); ALTER TABLE partitioned_table_1 ADD CONSTRAINT fkey_5 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_2);
ALTER TABLE citus_local_table_1 ADD CONSTRAINT fkey_6 FOREIGN KEY (col_1) REFERENCES citus_local_table_2(col_1);
SELECT undistribute_table('partitioned_table_1', cascade_via_foreign_keys=>true); SELECT undistribute_table('partitioned_table_1', cascade_via_foreign_keys=>true);
@ -44,5 +48,16 @@ $$
SELECT count(*) FROM pg_catalog.pg_tables WHERE schemaname='undistribute_table_cascade_mx' SELECT count(*) FROM pg_catalog.pg_tables WHERE schemaname='undistribute_table_cascade_mx'
$$); $$);
-- drop parititoned table as create_citus_local_table doesn't support partitioned tables
DROP TABLE partitioned_table_1;
SELECT create_citus_local_table('citus_local_table_1', cascade_via_foreign_keys=>true);
-- both workers should print 4 as we converted all tables except
-- partitioned table in this schema to a citus local table
SELECT run_command_on_workers(
$$
SELECT count(*) FROM pg_catalog.pg_tables WHERE schemaname='undistribute_table_cascade_mx'
$$);
-- cleanup at exit -- cleanup at exit
DROP SCHEMA undistribute_table_cascade_mx CASCADE; DROP SCHEMA undistribute_table_cascade_mx CASCADE;