mirror of https://github.com/citusdata/citus.git
Add support for ALTER TABLE commands defining foreign keys
parent
05931b8fe2
commit
36b418982f
|
@ -45,10 +45,23 @@
|
|||
/* Local functions forward declarations for unsupported command checks */
|
||||
static void PostprocessCreateTableStmtPartitionOf(CreateStmt *createStatement,
|
||||
const char *queryString);
|
||||
static void ErrorIfAlterTableDefinesFKeyFromPostgresToCitusLocalTable(
|
||||
static bool AlterTableDefinesFKeyBetweenPostgresAndNonDistTable(
|
||||
AlterTableStmt *alterTableStatement);
|
||||
static List * GetAlterTableStmtFKeyConstraintList(AlterTableStmt *alterTableStatement);
|
||||
static bool RelationIdListContainsCitusTableType(List *relationIdList,
|
||||
CitusTableType citusTableType);
|
||||
static bool RelationIdListContainsPostgresTable(List *relationIdList);
|
||||
static void ConvertPostgresLocalTablesToCitusLocalTables(
|
||||
AlterTableStmt *alterTableStatement);
|
||||
static int CompareRangeVarsByOid(const void *leftElement, const void *rightElement);
|
||||
static List * GetAlterTableAddFKeyRightRelationIdList(
|
||||
AlterTableStmt *alterTableStatement);
|
||||
static List * GetAlterTableAddFKeyRightRelationRangeVarList(
|
||||
AlterTableStmt *alterTableStatement);
|
||||
static List * GetAlterTableAddFKeyConstraintList(AlterTableStmt *alterTableStatement);
|
||||
static List * GetAlterTableCommandFKeyConstraintList(AlterTableCmd *command);
|
||||
static List * GetRangeVarListFromFKeyConstraintList(List *fKeyConstraintList);
|
||||
static List * GetRelationIdListFromRangeVarList(List *rangeVarList, LOCKMODE lockmode,
|
||||
bool missingOk);
|
||||
static bool AlterTableCommandTypeIsTrigger(AlterTableType alterTableType);
|
||||
static void ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement);
|
||||
static void ErrorIfCitusLocalTablePartitionCommand(AlterTableCmd *alterTableCmd,
|
||||
|
@ -154,7 +167,7 @@ PreprocessDropTableStmt(Node *node, const char *queryString,
|
|||
/*
|
||||
* PostprocessCreateTableStmt takes CreateStmt object as a parameter and errors
|
||||
* out if it creates a table with a foreign key that references to a citus local
|
||||
* table if pg version is older than 13 (see comment in function).
|
||||
* table.
|
||||
*
|
||||
* This function also processes CREATE TABLE ... PARTITION OF statements via
|
||||
* PostprocessCreateTableStmtPartitionOf function.
|
||||
|
@ -162,17 +175,6 @@ PreprocessDropTableStmt(Node *node, const char *queryString,
|
|||
void
|
||||
PostprocessCreateTableStmt(CreateStmt *createStatement, const char *queryString)
|
||||
{
|
||||
#if PG_VERSION_NUM < PG_VERSION_13
|
||||
|
||||
/*
|
||||
* Postgres processes foreign key constraints implied by CREATE TABLE
|
||||
* commands by internally executing ALTER TABLE commands via standard
|
||||
* process utility starting from PG13. Hence, we will already perform
|
||||
* unsupported foreign key checks via PreprocessAlterTableStmt function
|
||||
* in PG13. But for the older version, we need to do unsupported foreign
|
||||
* key checks here.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Relation must exist and it is already locked as standard process utility
|
||||
* is already executed.
|
||||
|
@ -183,7 +185,6 @@ PostprocessCreateTableStmt(CreateStmt *createStatement, const char *queryString)
|
|||
{
|
||||
ErrorOutForFKeyBetweenPostgresAndCitusLocalTable(relationId);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (createStatement->inhRelations != NIL && createStatement->partbound != NULL)
|
||||
{
|
||||
|
@ -378,14 +379,38 @@ PreprocessAlterTableStmt(Node *node, const char *alterTableCommand,
|
|||
leftRelationId = IndexGetRelation(leftRelationId, missingOk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Normally, we would do this check in ErrorIfUnsupportedForeignConstraintExists
|
||||
* in post process step. However, we skip doing error checks in post process if
|
||||
* this pre process returns NIL -and this method returns NIL if the left relation
|
||||
* is a postgres table. So, we need to error out for foreign keys from postgres
|
||||
* tables to citus local tables here.
|
||||
*/
|
||||
ErrorIfAlterTableDefinesFKeyFromPostgresToCitusLocalTable(alterTableStatement);
|
||||
if (processUtilityContext != PROCESS_UTILITY_SUBCOMMAND &&
|
||||
AlterTableDefinesFKeyBetweenPostgresAndNonDistTable(alterTableStatement) &&
|
||||
CoordinatorAddedAsWorkerNode())
|
||||
{
|
||||
/*
|
||||
* We don't process subcommands generated by postgres.
|
||||
* This is mainly because postgres started to issue ALTER TABLE commands
|
||||
* for some set of objects that are defined via CREATE TABLE commands as
|
||||
* of pg13. However, citus already has a separate logic for CREATE TABLE
|
||||
* commands.
|
||||
*
|
||||
* To support foreign keys from/to postgres local tables to/from reference
|
||||
* or citus local tables, we convert given postgres local table -and the
|
||||
* other postgres tables that it is connected via a fkey graph- to a citus
|
||||
* local table.
|
||||
*
|
||||
* Note that we don't convert postgres tables to citus local tables if
|
||||
* coordinator is not added to metadata as CreateCitusLocalTable requires
|
||||
* this. In this case, we assume user is about to create reference or
|
||||
* distributed table from local table and we don't want to break user
|
||||
* experience by asking to add coordinator to metadata.
|
||||
*/
|
||||
ConvertPostgresLocalTablesToCitusLocalTables(alterTableStatement);
|
||||
|
||||
/*
|
||||
* CreateCitusLocalTable converts relation to a shard relation and creates
|
||||
* shell table from scratch.
|
||||
* For this reason we should re-enter to PreprocessAlterTableStmt to operate
|
||||
* on shell table relation id.
|
||||
*/
|
||||
return PreprocessAlterTableStmt(node, alterTableCommand, processUtilityContext);
|
||||
}
|
||||
|
||||
bool referencingIsLocalTable = !IsCitusTable(leftRelationId);
|
||||
if (referencingIsLocalTable)
|
||||
|
@ -595,59 +620,249 @@ PreprocessAlterTableStmt(Node *node, const char *alterTableCommand,
|
|||
|
||||
|
||||
/*
|
||||
* ErrorIfAlterTableDefinesFKeyFromPostgresToCitusLocalTable errors out if
|
||||
* given ALTER TABLE statement defines foreign key from a postgres local table
|
||||
* to a citus local table.
|
||||
* AlterTableDefinesFKeyBetweenPostgresAndNonDistTable returns true if given
|
||||
* alter table command defines foreign key between a postgres table and a
|
||||
* reference or citus local table.
|
||||
*/
|
||||
static void
|
||||
ErrorIfAlterTableDefinesFKeyFromPostgresToCitusLocalTable(
|
||||
AlterTableStmt *alterTableStatement)
|
||||
static bool
|
||||
AlterTableDefinesFKeyBetweenPostgresAndNonDistTable(AlterTableStmt *alterTableStatement)
|
||||
{
|
||||
List *commandList = alterTableStatement->cmds;
|
||||
|
||||
LOCKMODE lockmode = AlterTableGetLockLevel(commandList);
|
||||
Oid leftRelationId = AlterTableLookupRelation(alterTableStatement, lockmode);
|
||||
|
||||
if (IsCitusTable(leftRelationId))
|
||||
List *foreignKeyConstraintList =
|
||||
GetAlterTableAddFKeyConstraintList(alterTableStatement);
|
||||
if (list_length(foreignKeyConstraintList) == 0)
|
||||
{
|
||||
/* left relation is not a postgres local table, */
|
||||
return;
|
||||
/* we are not defining any foreign keys */
|
||||
return false;
|
||||
}
|
||||
|
||||
List *alterTableFKeyConstraints =
|
||||
GetAlterTableStmtFKeyConstraintList(alterTableStatement);
|
||||
Constraint *constraint = NULL;
|
||||
foreach_ptr(constraint, alterTableFKeyConstraints)
|
||||
List *rightRelationIdList =
|
||||
GetAlterTableAddFKeyRightRelationIdList(alterTableStatement);
|
||||
|
||||
LOCKMODE lockmode = AlterTableGetLockLevel(alterTableStatement->cmds);
|
||||
Oid leftRelationId = AlterTableLookupRelation(alterTableStatement, lockmode);
|
||||
if (!IsCitusTable(leftRelationId))
|
||||
{
|
||||
Oid rightRelationId = RangeVarGetRelid(constraint->pktable, lockmode,
|
||||
alterTableStatement->missing_ok);
|
||||
if (IsCitusTableType(rightRelationId, CITUS_LOCAL_TABLE))
|
||||
return RelationIdListContainsCitusTableType(rightRelationIdList,
|
||||
CITUS_TABLE_WITH_NO_DIST_KEY);
|
||||
}
|
||||
else if (IsCitusTableType(leftRelationId, CITUS_TABLE_WITH_NO_DIST_KEY))
|
||||
{
|
||||
return RelationIdListContainsPostgresTable(rightRelationIdList);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RelationIdListContainsCitusTableType returns true if given relationIdList
|
||||
* contains a citus table with given type.
|
||||
*/
|
||||
static bool
|
||||
RelationIdListContainsCitusTableType(List *relationIdList, CitusTableType citusTableType)
|
||||
{
|
||||
Oid relationId = InvalidOid;
|
||||
foreach_oid(relationId, relationIdList)
|
||||
{
|
||||
if (IsCitusTableType(relationId, citusTableType))
|
||||
{
|
||||
ErrorOutForFKeyBetweenPostgresAndCitusLocalTable(leftRelationId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RelationIdListContainsPostgresTable returns true if given relationIdList
|
||||
* contains a postgres table.
|
||||
*/
|
||||
static bool
|
||||
RelationIdListContainsPostgresTable(List *relationIdList)
|
||||
{
|
||||
Oid relationId = InvalidOid;
|
||||
foreach_oid(relationId, relationIdList)
|
||||
{
|
||||
if (OidIsValid(relationId) && !IsCitusTable(relationId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ConvertPostgresLocalTablesToCitusLocalTables converts each postgres table
|
||||
* involved in foreign keys to be defined by given alter table command and the
|
||||
* other tables connected to them via a foreign key graph to citus local tables.
|
||||
*/
|
||||
static void
|
||||
ConvertPostgresLocalTablesToCitusLocalTables(AlterTableStmt *alterTableStatement)
|
||||
{
|
||||
List *rightRelationRangeVarList =
|
||||
GetAlterTableAddFKeyRightRelationRangeVarList(alterTableStatement);
|
||||
RangeVar *leftRelationRangeVar = alterTableStatement->relation;
|
||||
List *relationRangeVarList = lappend(rightRelationRangeVarList, leftRelationRangeVar);
|
||||
|
||||
/*
|
||||
* To prevent deadlocks, sort the list before converting each postgres local
|
||||
* table to a citus local table.
|
||||
*/
|
||||
relationRangeVarList = SortList(relationRangeVarList, CompareRangeVarsByOid);
|
||||
|
||||
/*
|
||||
* Here we should operate on RangeVar objects since relations oid's would
|
||||
* change in below loop due to CreateCitusLocalTable.
|
||||
*/
|
||||
RangeVar *relationRangeVar;
|
||||
foreach_ptr(relationRangeVar, relationRangeVarList)
|
||||
{
|
||||
List *commandList = alterTableStatement->cmds;
|
||||
LOCKMODE lockMode = AlterTableGetLockLevel(commandList);
|
||||
bool missingOk = alterTableStatement->missing_ok;
|
||||
Oid relationId = RangeVarGetRelid(relationRangeVar, lockMode, missingOk);
|
||||
if (!OidIsValid(relationId))
|
||||
{
|
||||
/*
|
||||
* As we are in preprocess, missingOk might be true and relation
|
||||
* might not exist.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
else if (IsCitusTable(relationId))
|
||||
{
|
||||
/*
|
||||
* relationRangeVarList has also reference and citus local tables
|
||||
* involved in this ADD FOREIGN KEY command. Moreover, even if
|
||||
* relationId was belonging to a postgres local table initially,
|
||||
* we might had already converted it to a citus local table by cascading.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* The only reason behind using a try/catch block here is giving a proper
|
||||
* error message. For example, when creating a citus local table we might
|
||||
* give an error telling that partitioned tables are not supported for
|
||||
* citus local table creation. But as a user it wouldn't make much sense
|
||||
* to see such an error. So here we extend error message to tell that we
|
||||
* actually ended up with this error when trying to define the foreign key.
|
||||
*
|
||||
* Also, as CopyErrorData() requires (CurrentMemoryContext != ErrorContext),
|
||||
* so we store CurrentMemoryContext here.
|
||||
*/
|
||||
MemoryContext savedMemoryContext = CurrentMemoryContext;
|
||||
PG_TRY();
|
||||
{
|
||||
bool cascade = true;
|
||||
CreateCitusLocalTable(relationId, cascade);
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
MemoryContextSwitchTo(savedMemoryContext);
|
||||
|
||||
ErrorData *errorData = CopyErrorData();
|
||||
FlushErrorState();
|
||||
|
||||
if (errorData->elevel != ERROR)
|
||||
{
|
||||
PG_RE_THROW();
|
||||
}
|
||||
|
||||
/* override error detail */
|
||||
errorData->detail = "When adding a foreign key from a local table to "
|
||||
"a reference table, Citus applies a conversion to "
|
||||
"all the local tables in the foreign key graph";
|
||||
ThrowErrorData(errorData);
|
||||
}
|
||||
PG_END_TRY();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GetAlterTableStmtFKeyConstraintList returns a list of Constraint objects for
|
||||
* the foreign keys that given ALTER TABLE statement defines.
|
||||
* CompareRangeVarsByOid is a comparison function to sort RangeVar object list.
|
||||
*/
|
||||
static int
|
||||
CompareRangeVarsByOid(const void *leftElement, const void *rightElement)
|
||||
{
|
||||
RangeVar *leftRangeVar = *((RangeVar **) leftElement);
|
||||
RangeVar *rightRangeVar = *((RangeVar **) rightElement);
|
||||
|
||||
/*
|
||||
* Any way we will check their existence, so it's okay to map non-existing
|
||||
* relations to InvalidOid when sorting.
|
||||
*/
|
||||
bool missingOk = true;
|
||||
|
||||
/*
|
||||
* As this is an object comparator function, there is no way to understand
|
||||
* proper lock mode. So assume caller already locked relations.
|
||||
*/
|
||||
LOCKMODE lockMode = NoLock;
|
||||
|
||||
Oid leftRelationId = RangeVarGetRelid(leftRangeVar, lockMode, missingOk);
|
||||
Oid rightRelationId = RangeVarGetRelid(rightRangeVar, lockMode, missingOk);
|
||||
return CompareOids(&leftRelationId, &rightRelationId);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GetAlterTableAddFKeyRightRelationIdList returns a list of oid's for right
|
||||
* relations involved in foreign keys to be defined by given ALTER TABLE command.
|
||||
*/
|
||||
static List *
|
||||
GetAlterTableStmtFKeyConstraintList(AlterTableStmt *alterTableStatement)
|
||||
GetAlterTableAddFKeyRightRelationIdList(AlterTableStmt *alterTableStatement)
|
||||
{
|
||||
List *alterTableFKeyConstraintList = NIL;
|
||||
List *rightRelationRangeVarList =
|
||||
GetAlterTableAddFKeyRightRelationRangeVarList(alterTableStatement);
|
||||
List *commandList = alterTableStatement->cmds;
|
||||
LOCKMODE lockMode = AlterTableGetLockLevel(commandList);
|
||||
bool missingOk = alterTableStatement->missing_ok;
|
||||
List *rightRelationIdList =
|
||||
GetRelationIdListFromRangeVarList(rightRelationRangeVarList, lockMode, missingOk);
|
||||
return rightRelationIdList;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GetAlterTableAddFKeyRightRelationRangeVarList returns a list of RangeVar
|
||||
* objects for right relations involved in foreign keys to be defined by
|
||||
* given ALTER TABLE command.
|
||||
*/
|
||||
static List *
|
||||
GetAlterTableAddFKeyRightRelationRangeVarList(AlterTableStmt *alterTableStatement)
|
||||
{
|
||||
List *fKeyConstraintList = GetAlterTableAddFKeyConstraintList(alterTableStatement);
|
||||
List *rightRelationRangeVarList =
|
||||
GetRangeVarListFromFKeyConstraintList(fKeyConstraintList);
|
||||
return rightRelationRangeVarList;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GetAlterTableAddFKeyConstraintList returns a list of Constraint objects for
|
||||
* foreign keys that given ALTER TABLE to be defined by given ALTER TABLE command.
|
||||
*/
|
||||
static List *
|
||||
GetAlterTableAddFKeyConstraintList(AlterTableStmt *alterTableStatement)
|
||||
{
|
||||
List *foreignKeyConstraintList = NIL;
|
||||
|
||||
List *commandList = alterTableStatement->cmds;
|
||||
AlterTableCmd *command = NULL;
|
||||
foreach_ptr(command, commandList)
|
||||
{
|
||||
List *commandFKeyConstraintList = GetAlterTableCommandFKeyConstraintList(command);
|
||||
alterTableFKeyConstraintList = list_concat(alterTableFKeyConstraintList,
|
||||
commandFKeyConstraintList);
|
||||
List *commandForeignKeyConstraintList =
|
||||
GetAlterTableCommandFKeyConstraintList(command);
|
||||
foreignKeyConstraintList = list_concat(foreignKeyConstraintList,
|
||||
commandForeignKeyConstraintList);
|
||||
}
|
||||
|
||||
return alterTableFKeyConstraintList;
|
||||
return foreignKeyConstraintList;
|
||||
}
|
||||
|
||||
|
||||
|
@ -692,6 +907,47 @@ GetAlterTableCommandFKeyConstraintList(AlterTableCmd *command)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* GetRangeVarListFromFKeyConstraintList returns a list of RangeVar objects for
|
||||
* right relations in fKeyConstraintList.
|
||||
*/
|
||||
static List *
|
||||
GetRangeVarListFromFKeyConstraintList(List *fKeyConstraintList)
|
||||
{
|
||||
List *rightRelationRangeVarList = NIL;
|
||||
|
||||
Constraint *fKeyConstraint = NULL;
|
||||
foreach_ptr(fKeyConstraint, fKeyConstraintList)
|
||||
{
|
||||
RangeVar *rightRelationRangeVar = fKeyConstraint->pktable;
|
||||
rightRelationRangeVarList = lappend(rightRelationRangeVarList,
|
||||
rightRelationRangeVar);
|
||||
}
|
||||
|
||||
return rightRelationRangeVarList;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GetRelationIdListFromRangeVarList returns relation id list for relations
|
||||
* identified by RangeVar objects in given list.
|
||||
*/
|
||||
static List *
|
||||
GetRelationIdListFromRangeVarList(List *rangeVarList, LOCKMODE lockMode, bool missingOk)
|
||||
{
|
||||
List *relationIdList = NIL;
|
||||
|
||||
RangeVar *rangeVar = NULL;
|
||||
foreach_ptr(rangeVar, rangeVarList)
|
||||
{
|
||||
Oid rightRelationId = RangeVarGetRelid(rangeVar, lockMode, missingOk);
|
||||
relationIdList = lappend_oid(relationIdList, rightRelationId);
|
||||
}
|
||||
|
||||
return relationIdList;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* AlterTableCommandTypeIsTrigger returns true if given alter table command type
|
||||
* is identifies an ALTER TABLE .. TRIGGER .. command.
|
||||
|
|
|
@ -484,4 +484,8 @@ extern void CreateCitusLocalTable(Oid relationId, bool cascadeViaForeignKeys);
|
|||
extern bool ShouldPropagateSetCommand(VariableSetStmt *setStmt);
|
||||
extern void PostprocessVariableSetStmt(VariableSetStmt *setStmt, const char *setCommand);
|
||||
|
||||
/* create_citus_local_table.c */
|
||||
|
||||
extern void CreateCitusLocalTable(Oid relationId, bool cascade);
|
||||
|
||||
#endif /*CITUS_COMMANDS_H */
|
||||
|
|
|
@ -413,8 +413,6 @@ NOTICE: executing the command locally: CREATE UNIQUE INDEX uniqueindex2_15040
|
|||
---- utility command execution ----
|
||||
---------------------------------------------------------------------
|
||||
SET search_path TO citus_local_tables_test_schema;
|
||||
-- any foreign key between citus local tables and other tables except reference tables cannot be set
|
||||
-- more tests at ref_citus_local_fkeys.sql
|
||||
-- between citus local tables and distributed tables
|
||||
ALTER TABLE citus_local_table_1 ADD CONSTRAINT fkey_c_to_dist FOREIGN KEY(a) references distributed_table(a);
|
||||
ERROR: cannot create foreign key constraint since foreign keys from reference tables and citus local tables to distributed tables are not supported
|
||||
|
@ -422,15 +420,15 @@ ALTER TABLE distributed_table ADD CONSTRAINT fkey_dist_to_c FOREIGN KEY(a) refer
|
|||
ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table
|
||||
-- between citus local tables and local tables
|
||||
ALTER TABLE citus_local_table_1 ADD CONSTRAINT fkey_c_to_local FOREIGN KEY(a) references local_table(a);
|
||||
ERROR: cannot create foreign key constraint as "local_table" is a postgres local table
|
||||
NOTICE: executing the command locally: SELECT worker_apply_inter_shard_ddl_command (1504027, 'citus_local_tables_test_schema', 1504037, 'citus_local_tables_test_schema', 'ALTER TABLE citus_local_table_1 ADD CONSTRAINT fkey_c_to_local FOREIGN KEY(a) references local_table(a);')
|
||||
ALTER TABLE local_table
|
||||
ADD CONSTRAINT fkey_local_to_c FOREIGN KEY(a) references citus_local_table_1(a),
|
||||
ADD CONSTRAINT fkey_self FOREIGN KEY(a) references local_table(a);
|
||||
ERROR: cannot create foreign key constraint as "local_table" is a postgres local table
|
||||
ERROR: cannot execute ADD CONSTRAINT command with other subcommands
|
||||
ALTER TABLE local_table
|
||||
ADD COLUMN b int references citus_local_table_1(a),
|
||||
ADD COLUMN c int references local_table(a);
|
||||
ERROR: cannot create foreign key constraint as "local_table" is a postgres local table
|
||||
ERROR: cannot execute ADD COLUMN command with PRIMARY KEY, UNIQUE, FOREIGN and CHECK constraints
|
||||
CREATE TABLE local_table_4 (
|
||||
a int unique references citus_local_table_1(a),
|
||||
b int references local_table_4(a));
|
||||
|
@ -478,6 +476,8 @@ ORDER BY 1;
|
|||
TRUNCATE citus_local_table_1, citus_local_table_2, distributed_table, local_table, reference_table;
|
||||
NOTICE: executing the command locally: TRUNCATE TABLE citus_local_tables_test_schema.citus_local_table_1_xxxxx CASCADE
|
||||
NOTICE: executing the command locally: TRUNCATE TABLE citus_local_tables_test_schema.citus_local_table_2_xxxxx CASCADE
|
||||
NOTICE: executing the command locally: TRUNCATE TABLE citus_local_tables_test_schema.local_table_xxxxx CASCADE
|
||||
NOTICE: truncate cascades to table "citus_local_table_1_xxxxxxx"
|
||||
NOTICE: executing the command locally: TRUNCATE TABLE citus_local_tables_test_schema.reference_table_xxxxx CASCADE
|
||||
-- test vacuum
|
||||
VACUUM citus_local_table_1;
|
||||
|
@ -485,6 +485,8 @@ VACUUM citus_local_table_1, distributed_table, local_table, reference_table;
|
|||
-- test drop
|
||||
DROP TABLE citus_local_table_1, citus_local_table_2, distributed_table, local_table, reference_table;
|
||||
NOTICE: executing the command locally: DROP TABLE IF EXISTS citus_local_tables_test_schema.reference_table_xxxxx CASCADE
|
||||
NOTICE: executing the command locally: DROP TABLE IF EXISTS citus_local_tables_test_schema.local_table_xxxxx CASCADE
|
||||
NOTICE: drop cascades to constraint fkey_c_to_local_1504027 on table citus_local_tables_test_schema.citus_local_table_1_1504027
|
||||
NOTICE: executing the command locally: DROP TABLE IF EXISTS citus_local_tables_test_schema.citus_local_table_2_xxxxx CASCADE
|
||||
NOTICE: executing the command locally: DROP TABLE IF EXISTS citus_local_tables_test_schema.citus_local_table_1_xxxxx CASCADE
|
||||
-- test some other udf's with citus local tables
|
||||
|
@ -498,9 +500,9 @@ SELECT create_citus_local_table('citus_local_table_4');
|
|||
-- should work --
|
||||
-- insert some data & create an index for table size udf's
|
||||
INSERT INTO citus_local_table_4 VALUES (1), (2), (3);
|
||||
NOTICE: executing the command locally: INSERT INTO citus_local_tables_test_schema.citus_local_table_4_1504037 AS citus_table_alias (a) VALUES (1), (2), (3)
|
||||
NOTICE: executing the command locally: INSERT INTO citus_local_tables_test_schema.citus_local_table_4_1504038 AS citus_table_alias (a) VALUES (1), (2), (3)
|
||||
CREATE INDEX citus_local_table_4_idx ON citus_local_table_4(a);
|
||||
NOTICE: executing the command locally: CREATE INDEX citus_local_table_4_idx_1504037 ON citus_local_tables_test_schema.citus_local_table_4_1504037 USING btree (a )
|
||||
NOTICE: executing the command locally: CREATE INDEX citus_local_table_4_idx_1504038 ON citus_local_tables_test_schema.citus_local_table_4_1504038 USING btree (a )
|
||||
SELECT citus_table_size('citus_local_table_4');
|
||||
citus_table_size
|
||||
---------------------------------------------------------------------
|
||||
|
@ -587,7 +589,7 @@ BEGIN;
|
|||
SELECT tableName FROM pg_catalog.pg_tables WHERE tablename LIKE 'citus_local_table_4%';
|
||||
tablename
|
||||
---------------------------------------------------------------------
|
||||
citus_local_table_4_1504037
|
||||
citus_local_table_4_1504038
|
||||
(1 row)
|
||||
|
||||
ROLLBACK;
|
||||
|
@ -596,7 +598,7 @@ SELECT shardid, get_colocated_shard_array(shardid)
|
|||
FROM (SELECT shardid FROM pg_dist_shard WHERE logicalrelid='citus_local_table_4'::regclass) as shardid;
|
||||
shardid | get_colocated_shard_array
|
||||
---------------------------------------------------------------------
|
||||
1504037 | {1504037}
|
||||
1504038 | {1504038}
|
||||
(1 row)
|
||||
|
||||
BEGIN;
|
||||
|
@ -626,7 +628,7 @@ ERROR: cannot delete from table
|
|||
CREATE TABLE postgres_local_table (a int);
|
||||
SELECT master_append_table_to_shard(shardId, 'postgres_local_table', 'localhost', :master_port)
|
||||
FROM (SELECT shardid FROM pg_dist_shard WHERE logicalrelid='citus_local_table_4'::regclass) as shardid;
|
||||
ERROR: cannot append to shardId 1504037
|
||||
ERROR: cannot append to shardId 1504038
|
||||
-- return true
|
||||
SELECT citus_table_is_visible('citus_local_table_4'::regclass::oid);
|
||||
citus_table_is_visible
|
||||
|
@ -667,7 +669,7 @@ SELECT create_citus_local_table('referenced_table');
|
|||
(1 row)
|
||||
|
||||
ALTER TABLE referencing_table ADD CONSTRAINT fkey_cl_to_cl FOREIGN KEY (a) REFERENCES referenced_table(a);
|
||||
NOTICE: executing the command locally: SELECT worker_apply_inter_shard_ddl_command (1504038, 'citus_local_tables_test_schema', 1504039, 'citus_local_tables_test_schema', 'ALTER TABLE referencing_table ADD CONSTRAINT fkey_cl_to_cl FOREIGN KEY (a) REFERENCES referenced_table(a);')
|
||||
NOTICE: executing the command locally: SELECT worker_apply_inter_shard_ddl_command (1504039, 'citus_local_tables_test_schema', 1504040, 'citus_local_tables_test_schema', 'ALTER TABLE referencing_table ADD CONSTRAINT fkey_cl_to_cl FOREIGN KEY (a) REFERENCES referenced_table(a);')
|
||||
-- observe the debug messages telling that we switch to sequential
|
||||
-- execution when truncating a citus local table that is referenced
|
||||
-- by another table
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
\set VERBOSITY terse
|
||||
SET citus.next_shard_id TO 1518000;
|
||||
SET citus.shard_replication_factor TO 1;
|
||||
CREATE SCHEMA fkeys_between_local_ref;
|
||||
SET search_path TO fkeys_between_local_ref;
|
||||
SET client_min_messages to ERROR;
|
||||
-- create a view for testing
|
||||
CREATE VIEW citus_local_tables_in_schema AS
|
||||
SELECT logicalrelid FROM pg_dist_partition, pg_tables
|
||||
WHERE tablename=logicalrelid::regclass::text AND
|
||||
schemaname='fkeys_between_local_ref' AND
|
||||
partmethod = 'n' AND repmodel = 'c';
|
||||
-- remove coordinator if it is added to pg_dist_node and test
|
||||
-- behavior when coordinator is not added to metadata
|
||||
SELECT COUNT(master_remove_node(nodename, nodeport)) < 2
|
||||
FROM pg_dist_node WHERE nodename='localhost' AND nodeport=:master_port;
|
||||
?column?
|
||||
---------------------------------------------------------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
create table ref (a int primary key);
|
||||
select create_reference_table('ref');
|
||||
create_reference_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- creating local table that references to reference table is supported
|
||||
create table other (x int primary key, y int);
|
||||
-- creating reference table from a local table that references
|
||||
-- to reference table is supported
|
||||
alter table other add constraint fk foreign key (y) references ref (a) on delete cascade;
|
||||
select create_reference_table('other');
|
||||
create_reference_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
drop table if exists ref, ref2 cascade;
|
||||
create table ref (a int primary key);
|
||||
create table ref2 (x int);
|
||||
alter table ref2 add constraint fk foreign key (x) references ref (a);
|
||||
select create_reference_table('ref');
|
||||
create_reference_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- we can also define more foreign keys after creating reference
|
||||
-- table from referenced table
|
||||
alter table ref2 add constraint fk2 foreign key (x) references ref (a);
|
||||
-- then we can create reference table from referencing table
|
||||
select create_reference_table('ref2');
|
||||
create_reference_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
drop table if exists ref, ref2, other cascade;
|
||||
-- add coordinator to pg_dist_node for rest of the tests
|
||||
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);
|
||||
INSERT INTO local_table_1 SELECT i FROM generate_series(195, 205) i;
|
||||
INSERT INTO local_table_2 SELECT i FROM generate_series(195, 205) i;
|
||||
INSERT INTO local_table_3 SELECT i FROM generate_series(195, 205) i;
|
||||
INSERT INTO local_table_4 SELECT i FROM generate_series(195, 205) i;
|
||||
-- _
|
||||
-- | |
|
||||
-- | 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);
|
||||
CREATE TABLE reference_table_1(col_1 INT UNIQUE, col_2 INT);
|
||||
INSERT INTO reference_table_1 SELECT i FROM generate_series(195, 205) i;
|
||||
SELECT create_reference_table('reference_table_1');
|
||||
create_reference_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE TABLE partitioned_table_1 (col_1 INT, 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_200_300 PARTITION OF partitioned_table_1 FOR VALUES FROM (200) TO (300);
|
||||
INSERT INTO partitioned_table_1 SELECT i FROM generate_series(195, 205) i;
|
||||
ALTER TABLE partitioned_table_1 ADD CONSTRAINT fkey_8 FOREIGN KEY (col_1) REFERENCES local_table_4(col_1);
|
||||
-- now that we attached partitioned table to graph below errors out
|
||||
-- since we cannot create citus local table from partitioned tables
|
||||
ALTER TABLE reference_table_1 ADD CONSTRAINT fkey_9 FOREIGN KEY (col_1) REFERENCES local_table_1(col_1);
|
||||
ERROR: cannot create citus local table "partitioned_table_1", only regular tables and foreign tables are supported for citus local table creation
|
||||
ALTER TABLE partitioned_table_1 DROP CONSTRAINT fkey_8;
|
||||
BEGIN;
|
||||
-- now that we detached partitioned table from graph, succeeds
|
||||
ALTER TABLE reference_table_1 ADD CONSTRAINT fkey_10 FOREIGN KEY (col_1) REFERENCES local_table_1(col_1);
|
||||
-- show that we converted all 4 local tables in this schema to citus local tables
|
||||
SELECT COUNT(*)=4 FROM citus_local_tables_in_schema;
|
||||
?column?
|
||||
---------------------------------------------------------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
ROLLBACK;
|
||||
-- this actually attempts to convert local tables to citus local tables but errors out
|
||||
-- as citus doesn't support defining foreign keys via add column commands
|
||||
ALTER TABLE local_table_1 ADD COLUMN col_3 INT REFERENCES reference_table_1(col_1);
|
||||
ERROR: cannot execute ADD COLUMN command with PRIMARY KEY, UNIQUE, FOREIGN and CHECK constraints
|
||||
BEGIN;
|
||||
-- define a foreign key so that all 4 local tables become citus local tables
|
||||
ALTER TABLE local_table_1 ADD CONSTRAINT fkey_11 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_1);
|
||||
CREATE TABLE local_table_5 (col_1 INT UNIQUE);
|
||||
-- now define foreign key from local to citus local table
|
||||
ALTER TABLE local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES local_table_2(col_1);
|
||||
-- now we have 5 citus local tables in this schema
|
||||
SELECT COUNT(*)=5 FROM citus_local_tables_in_schema;
|
||||
?column?
|
||||
---------------------------------------------------------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
ROLLBACK;
|
||||
-- they fail as local_table_99 does not exist
|
||||
ALTER TABLE local_table_99 ADD CONSTRAINT fkey FOREIGN KEY (col_1) REFERENCES reference_table_1(col_1);
|
||||
ERROR: relation "local_table_99" does not exist
|
||||
ALTER TABLE local_table_1 ADD CONSTRAINT fkey FOREIGN KEY (col_1) REFERENCES local_table_99(col_1);
|
||||
ERROR: relation "local_table_99" does not exist
|
||||
-- they fail as col_99 does not exist
|
||||
ALTER TABLE local_table_1 ADD CONSTRAINT fkey FOREIGN KEY (col_99) REFERENCES reference_table_1(col_1);
|
||||
ERROR: column "col_99" referenced in foreign key constraint does not exist
|
||||
ALTER TABLE local_table_1 ADD CONSTRAINT fkey FOREIGN KEY (col_1) REFERENCES reference_table_1(col_99);
|
||||
ERROR: column "col_99" referenced in foreign key constraint does not exist
|
||||
-- fails as col_2 does not have a unique/primary key constraint
|
||||
ALTER TABLE local_table_1 ADD CONSTRAINT fkey FOREIGN KEY (col_1) REFERENCES reference_table_1(col_2);
|
||||
ERROR: there is no unique constraint matching given keys for referenced table "reference_table_1"
|
||||
CREATE TABLE reference_table_2 (col_1 INT UNIQUE, col_2 INT);
|
||||
INSERT INTO reference_table_2 SELECT i FROM generate_series(195, 205) i;
|
||||
BEGIN;
|
||||
-- define foreign key when both ends are local tables
|
||||
ALTER TABLE local_table_1 ADD CONSTRAINT fkey_11 FOREIGN KEY (col_1) REFERENCES reference_table_2(col_1);
|
||||
-- we still don't convert local tables to citus local tables when
|
||||
-- creating reference tables
|
||||
SELECT create_reference_table('reference_table_2');
|
||||
create_reference_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- now defining another foreign key would convert local tables to
|
||||
-- citus local tables as reference_table_2 is now a reference table
|
||||
ALTER TABLE local_table_1 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES reference_table_2(col_1);
|
||||
-- now print metadata to show that everyting is fine
|
||||
SELECT logicalrelid::text AS tablename, partmethod, repmodel FROM pg_dist_partition
|
||||
WHERE logicalrelid::text IN (SELECT tablename FROM pg_tables WHERE schemaname='fkeys_between_local_ref')
|
||||
ORDER BY tablename;
|
||||
tablename | partmethod | repmodel
|
||||
---------------------------------------------------------------------
|
||||
local_table_1 | n | c
|
||||
local_table_2 | n | c
|
||||
local_table_3 | n | c
|
||||
local_table_4 | n | c
|
||||
reference_table_1 | n | t
|
||||
reference_table_2 | n | t
|
||||
(6 rows)
|
||||
|
||||
ROLLBACK;
|
||||
-- we don't support foreign keys from citus local to reference tables
|
||||
-- with ON DELETE/UPDATE CASCADE behavior, so below two errors out
|
||||
BEGIN;
|
||||
SELECT create_reference_table('reference_table_2');
|
||||
create_reference_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
ALTER TABLE reference_table_2 ADD CONSTRAINT fkey_11 FOREIGN KEY (col_1) REFERENCES local_table_2(col_1) ON DELETE CASCADE;
|
||||
ERROR: cannot define foreign key constraint, foreign keys from reference tables to citus local tables can only be defined with NO ACTION or RESTRICT behaviors
|
||||
ROLLBACK;
|
||||
BEGIN;
|
||||
SELECT create_reference_table('reference_table_2');
|
||||
create_reference_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
ALTER TABLE reference_table_2 ADD CONSTRAINT fkey_local_to_ref FOREIGN KEY (col_1) REFERENCES local_table_2(col_1) ON UPDATE CASCADE;
|
||||
ERROR: cannot define foreign key constraint, foreign keys from reference tables to citus local tables can only be defined with NO ACTION or RESTRICT behaviors
|
||||
ROLLBACK;
|
||||
-- but we support such foreign key behaviors when foreign key is from
|
||||
-- citus local to reference table
|
||||
BEGIN;
|
||||
SELECT create_reference_table('reference_table_2');
|
||||
create_reference_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
ALTER TABLE local_table_2 ADD CONSTRAINT fkey_11 FOREIGN KEY (col_1) REFERENCES reference_table_2(col_1) ON DELETE CASCADE;
|
||||
DELETE FROM reference_table_2 WHERE col_1=200;
|
||||
-- we deleted one row as DELETE cascades, so we should have 10 rows
|
||||
SELECT COUNT(*) FROM local_table_2;
|
||||
count
|
||||
---------------------------------------------------------------------
|
||||
10
|
||||
(1 row)
|
||||
|
||||
ROLLBACK;
|
||||
BEGIN;
|
||||
SELECT create_reference_table('reference_table_2');
|
||||
create_reference_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
ALTER TABLE local_table_2 ADD CONSTRAINT fkey_11 FOREIGN KEY (col_1) REFERENCES reference_table_2(col_1) ON UPDATE CASCADE;
|
||||
ROLLBACK;
|
||||
-- cleanup at exit
|
||||
DROP SCHEMA fkeys_between_local_ref CASCADE;
|
|
@ -26,17 +26,17 @@ DROP TABLE referencing_table;
|
|||
CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON UPDATE SET NULL);
|
||||
SELECT create_distributed_table('referencing_table', 'ref_id', 'hash');
|
||||
ERROR: cannot create foreign key constraint
|
||||
DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint.
|
||||
DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint.
|
||||
DROP TABLE referencing_table;
|
||||
CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON UPDATE SET DEFAULT);
|
||||
SELECT create_distributed_table('referencing_table', 'ref_id', 'hash');
|
||||
ERROR: cannot create foreign key constraint
|
||||
DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint.
|
||||
DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint.
|
||||
DROP TABLE referencing_table;
|
||||
CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON UPDATE CASCADE);
|
||||
SELECT create_distributed_table('referencing_table', 'ref_id', 'hash');
|
||||
ERROR: cannot create foreign key constraint
|
||||
DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint.
|
||||
DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint.
|
||||
DROP TABLE referencing_table;
|
||||
-- self referencing table with replication factor > 1
|
||||
CREATE TABLE self_referencing_table(id int, ref_id int, PRIMARY KEY (id, ref_id), FOREIGN KEY(id,ref_id) REFERENCES self_referencing_table(id, ref_id));
|
||||
|
@ -448,13 +448,13 @@ ERROR: cannot create foreign key constraint
|
|||
DETAIL: SET NULL or SET DEFAULT is not supported in ON DELETE operation when distribution key is included in the foreign key constraint
|
||||
ALTER TABLE referencing_table ADD CONSTRAINT test_constraint FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON UPDATE SET NULL;
|
||||
ERROR: cannot create foreign key constraint
|
||||
DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint.
|
||||
DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint.
|
||||
ALTER TABLE referencing_table ADD CONSTRAINT test_constraint FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON UPDATE SET DEFAULT;
|
||||
ERROR: cannot create foreign key constraint
|
||||
DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint.
|
||||
DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint.
|
||||
ALTER TABLE referencing_table ADD CONSTRAINT test_constraint FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON UPDATE CASCADE;
|
||||
ERROR: cannot create foreign key constraint
|
||||
DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint.
|
||||
DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint.
|
||||
-- test foreign constraint creation while adding the column
|
||||
ALTER TABLE referencing_table ADD COLUMN referencing_col int REFERENCES referenced_table(id) ON UPDATE CASCADE;;
|
||||
ERROR: cannot create foreign key constraint
|
||||
|
|
|
@ -327,6 +327,7 @@ test: citus_local_tables
|
|||
test: multi_row_router_insert mixed_relkind_tests create_ref_dist_from_citus_local
|
||||
test: undistribute_table_cascade
|
||||
test: create_citus_local_table_cascade
|
||||
test: fkeys_between_local_ref
|
||||
|
||||
test: remove_coordinator
|
||||
|
||||
|
|
|
@ -313,9 +313,6 @@ CREATE UNIQUE INDEX uniqueIndex2 ON "LocalTabLE.1!?!"(id);
|
|||
|
||||
SET search_path TO citus_local_tables_test_schema;
|
||||
|
||||
-- any foreign key between citus local tables and other tables except reference tables cannot be set
|
||||
-- more tests at ref_citus_local_fkeys.sql
|
||||
|
||||
-- between citus local tables and distributed tables
|
||||
ALTER TABLE citus_local_table_1 ADD CONSTRAINT fkey_c_to_dist FOREIGN KEY(a) references distributed_table(a);
|
||||
ALTER TABLE distributed_table ADD CONSTRAINT fkey_dist_to_c FOREIGN KEY(a) references citus_local_table_1(a);
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
\set VERBOSITY terse
|
||||
|
||||
SET citus.next_shard_id TO 1518000;
|
||||
SET citus.shard_replication_factor TO 1;
|
||||
|
||||
CREATE SCHEMA fkeys_between_local_ref;
|
||||
SET search_path TO fkeys_between_local_ref;
|
||||
|
||||
SET client_min_messages to ERROR;
|
||||
|
||||
-- create a view for testing
|
||||
CREATE VIEW citus_local_tables_in_schema AS
|
||||
SELECT logicalrelid FROM pg_dist_partition, pg_tables
|
||||
WHERE tablename=logicalrelid::regclass::text AND
|
||||
schemaname='fkeys_between_local_ref' AND
|
||||
partmethod = 'n' AND repmodel = 'c';
|
||||
|
||||
|
||||
-- remove coordinator if it is added to pg_dist_node and test
|
||||
-- behavior when coordinator is not added to metadata
|
||||
SELECT COUNT(master_remove_node(nodename, nodeport)) < 2
|
||||
FROM pg_dist_node WHERE nodename='localhost' AND nodeport=:master_port;
|
||||
|
||||
create table ref (a int primary key);
|
||||
select create_reference_table('ref');
|
||||
-- creating local table that references to reference table is supported
|
||||
create table other (x int primary key, y int);
|
||||
|
||||
-- creating reference table from a local table that references
|
||||
-- to reference table is supported
|
||||
alter table other add constraint fk foreign key (y) references ref (a) on delete cascade;
|
||||
select create_reference_table('other');
|
||||
|
||||
drop table if exists ref, ref2 cascade;
|
||||
|
||||
create table ref (a int primary key);
|
||||
create table ref2 (x int);
|
||||
alter table ref2 add constraint fk foreign key (x) references ref (a);
|
||||
select create_reference_table('ref');
|
||||
-- we can also define more foreign keys after creating reference
|
||||
-- table from referenced table
|
||||
alter table ref2 add constraint fk2 foreign key (x) references ref (a);
|
||||
-- then we can create reference table from referencing table
|
||||
select create_reference_table('ref2');
|
||||
|
||||
drop table if exists ref, ref2, other cascade;
|
||||
|
||||
|
||||
-- add coordinator to pg_dist_node for rest of the tests
|
||||
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);
|
||||
INSERT INTO local_table_1 SELECT i FROM generate_series(195, 205) i;
|
||||
INSERT INTO local_table_2 SELECT i FROM generate_series(195, 205) i;
|
||||
INSERT INTO local_table_3 SELECT i FROM generate_series(195, 205) i;
|
||||
INSERT INTO local_table_4 SELECT i FROM generate_series(195, 205) i;
|
||||
|
||||
-- _
|
||||
-- | |
|
||||
-- | 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);
|
||||
|
||||
CREATE TABLE reference_table_1(col_1 INT UNIQUE, col_2 INT);
|
||||
INSERT INTO reference_table_1 SELECT i FROM generate_series(195, 205) i;
|
||||
SELECT create_reference_table('reference_table_1');
|
||||
|
||||
CREATE TABLE partitioned_table_1 (col_1 INT, 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_200_300 PARTITION OF partitioned_table_1 FOR VALUES FROM (200) TO (300);
|
||||
INSERT INTO partitioned_table_1 SELECT i FROM generate_series(195, 205) i;
|
||||
|
||||
ALTER TABLE partitioned_table_1 ADD CONSTRAINT fkey_8 FOREIGN KEY (col_1) REFERENCES local_table_4(col_1);
|
||||
|
||||
-- now that we attached partitioned table to graph below errors out
|
||||
-- since we cannot create citus local table from partitioned tables
|
||||
ALTER TABLE reference_table_1 ADD CONSTRAINT fkey_9 FOREIGN KEY (col_1) REFERENCES local_table_1(col_1);
|
||||
|
||||
ALTER TABLE partitioned_table_1 DROP CONSTRAINT fkey_8;
|
||||
|
||||
BEGIN;
|
||||
-- now that we detached partitioned table from graph, succeeds
|
||||
ALTER TABLE reference_table_1 ADD CONSTRAINT fkey_10 FOREIGN KEY (col_1) REFERENCES local_table_1(col_1);
|
||||
|
||||
-- show that we converted all 4 local tables in this schema to citus local tables
|
||||
SELECT COUNT(*)=4 FROM citus_local_tables_in_schema;
|
||||
ROLLBACK;
|
||||
|
||||
-- this actually attempts to convert local tables to citus local tables but errors out
|
||||
-- as citus doesn't support defining foreign keys via add column commands
|
||||
ALTER TABLE local_table_1 ADD COLUMN col_3 INT REFERENCES reference_table_1(col_1);
|
||||
|
||||
BEGIN;
|
||||
-- define a foreign key so that all 4 local tables become citus local tables
|
||||
ALTER TABLE local_table_1 ADD CONSTRAINT fkey_11 FOREIGN KEY (col_1) REFERENCES reference_table_1(col_1);
|
||||
|
||||
CREATE TABLE local_table_5 (col_1 INT UNIQUE);
|
||||
-- now define foreign key from local to citus local table
|
||||
ALTER TABLE local_table_5 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES local_table_2(col_1);
|
||||
|
||||
-- now we have 5 citus local tables in this schema
|
||||
SELECT COUNT(*)=5 FROM citus_local_tables_in_schema;
|
||||
ROLLBACK;
|
||||
|
||||
-- they fail as local_table_99 does not exist
|
||||
ALTER TABLE local_table_99 ADD CONSTRAINT fkey FOREIGN KEY (col_1) REFERENCES reference_table_1(col_1);
|
||||
ALTER TABLE local_table_1 ADD CONSTRAINT fkey FOREIGN KEY (col_1) REFERENCES local_table_99(col_1);
|
||||
|
||||
-- they fail as col_99 does not exist
|
||||
ALTER TABLE local_table_1 ADD CONSTRAINT fkey FOREIGN KEY (col_99) REFERENCES reference_table_1(col_1);
|
||||
ALTER TABLE local_table_1 ADD CONSTRAINT fkey FOREIGN KEY (col_1) REFERENCES reference_table_1(col_99);
|
||||
|
||||
-- fails as col_2 does not have a unique/primary key constraint
|
||||
ALTER TABLE local_table_1 ADD CONSTRAINT fkey FOREIGN KEY (col_1) REFERENCES reference_table_1(col_2);
|
||||
|
||||
CREATE TABLE reference_table_2 (col_1 INT UNIQUE, col_2 INT);
|
||||
INSERT INTO reference_table_2 SELECT i FROM generate_series(195, 205) i;
|
||||
|
||||
BEGIN;
|
||||
-- define foreign key when both ends are local tables
|
||||
ALTER TABLE local_table_1 ADD CONSTRAINT fkey_11 FOREIGN KEY (col_1) REFERENCES reference_table_2(col_1);
|
||||
|
||||
-- we still don't convert local tables to citus local tables when
|
||||
-- creating reference tables
|
||||
SELECT create_reference_table('reference_table_2');
|
||||
|
||||
-- now defining another foreign key would convert local tables to
|
||||
-- citus local tables as reference_table_2 is now a reference table
|
||||
ALTER TABLE local_table_1 ADD CONSTRAINT fkey_12 FOREIGN KEY (col_1) REFERENCES reference_table_2(col_1);
|
||||
|
||||
-- now print metadata to show that everyting is fine
|
||||
SELECT logicalrelid::text AS tablename, partmethod, repmodel FROM pg_dist_partition
|
||||
WHERE logicalrelid::text IN (SELECT tablename FROM pg_tables WHERE schemaname='fkeys_between_local_ref')
|
||||
ORDER BY tablename;
|
||||
ROLLBACK;
|
||||
|
||||
-- we don't support foreign keys from citus local to reference tables
|
||||
-- with ON DELETE/UPDATE CASCADE behavior, so below two errors out
|
||||
|
||||
BEGIN;
|
||||
SELECT create_reference_table('reference_table_2');
|
||||
ALTER TABLE reference_table_2 ADD CONSTRAINT fkey_11 FOREIGN KEY (col_1) REFERENCES local_table_2(col_1) ON DELETE CASCADE;
|
||||
ROLLBACK;
|
||||
|
||||
BEGIN;
|
||||
SELECT create_reference_table('reference_table_2');
|
||||
ALTER TABLE reference_table_2 ADD CONSTRAINT fkey_local_to_ref FOREIGN KEY (col_1) REFERENCES local_table_2(col_1) ON UPDATE CASCADE;
|
||||
ROLLBACK;
|
||||
|
||||
-- but we support such foreign key behaviors when foreign key is from
|
||||
-- citus local to reference table
|
||||
|
||||
BEGIN;
|
||||
SELECT create_reference_table('reference_table_2');
|
||||
ALTER TABLE local_table_2 ADD CONSTRAINT fkey_11 FOREIGN KEY (col_1) REFERENCES reference_table_2(col_1) ON DELETE CASCADE;
|
||||
|
||||
DELETE FROM reference_table_2 WHERE col_1=200;
|
||||
-- we deleted one row as DELETE cascades, so we should have 10 rows
|
||||
SELECT COUNT(*) FROM local_table_2;
|
||||
ROLLBACK;
|
||||
|
||||
BEGIN;
|
||||
SELECT create_reference_table('reference_table_2');
|
||||
ALTER TABLE local_table_2 ADD CONSTRAINT fkey_11 FOREIGN KEY (col_1) REFERENCES reference_table_2(col_1) ON UPDATE CASCADE;
|
||||
ROLLBACK;
|
||||
|
||||
-- cleanup at exit
|
||||
DROP SCHEMA fkeys_between_local_ref CASCADE;
|
Loading…
Reference in New Issue