From 8f7e69fd0d6cc807af69defc2fcecc209f8594f4 Mon Sep 17 00:00:00 2001 From: velioglu Date: Fri, 17 Mar 2017 16:03:36 +0300 Subject: [PATCH] Initial commit Disregard it. --- .../commands/create_distributed_table.c | 5 +- .../distributed/executor/multi_utility.c | 233 ++++-------------- .../distributed/create_distributed_table.h | 16 ++ 3 files changed, 71 insertions(+), 183 deletions(-) create mode 100644 src/include/distributed/create_distributed_table.h diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index 4198837b2..d2ae45da8 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -35,6 +35,7 @@ #include "commands/extension.h" #include "commands/trigger.h" #include "distributed/colocation_utils.h" +#include "distributed/create_distributed_table.h" #include "distributed/distribution_column.h" #include "distributed/master_metadata_utility.h" #include "distributed/master_protocol.h" @@ -81,8 +82,6 @@ static char LookupDistributionMethod(Oid distributionMethodOid); static Oid SupportFunctionForColumn(Var *partitionColumn, Oid accessMethodId, int16 supportFunctionNumber); static bool LocalTableEmpty(Oid tableId); -static void ErrorIfNotSupportedConstraint(Relation relation, char distributionMethod, - Var *distributionColumn, uint32 colocationId); static void ErrorIfNotSupportedForeignConstraint(Relation relation, char distributionMethod, Var *distributionColumn, @@ -438,7 +437,7 @@ ConvertToDistributedTable(Oid relationId, char *distributionColumnName, * ii. Second, INSERT INTO .. ON CONFLICT (i.e., UPSERT) queries can be executed * with no further check for constraints. */ -static void +void ErrorIfNotSupportedConstraint(Relation relation, char distributionMethod, Var *distributionColumn, uint32 colocationId) { diff --git a/src/backend/distributed/executor/multi_utility.c b/src/backend/distributed/executor/multi_utility.c index b87809859..8a1bcc864 100644 --- a/src/backend/distributed/executor/multi_utility.c +++ b/src/backend/distributed/executor/multi_utility.c @@ -32,6 +32,7 @@ #include "commands/prepare.h" #include "distributed/citus_ruleutils.h" #include "distributed/colocation_utils.h" +#include "distributed/create_distributed_table.h" #include "distributed/master_metadata_utility.h" #include "distributed/master_protocol.h" #include "distributed/metadata_cache.h" @@ -370,6 +371,15 @@ multi_ProcessUtility(Node *parsetree, standard_ProcessUtility(parsetree, queryString, context, params, dest, completionTag); + /* we control alter table statement here to use same checks with creating table */ + if (IsA(parsetree, AlterTableStmt)) + { + AlterTableStmt *alterTableStatement = (AlterTableStmt *) parsetree; + + ErrorIfUnsupportedAlterTableStmt(alterTableStatement); + } + + if (commandMustRunAsOwner) { SetUserIdAndSecContext(savedUserId, savedSecurityContext); @@ -816,11 +826,9 @@ PlanAlterTableStmt(AlterTableStmt *alterTableStatement, const char *alterTableCo return NULL; } - ErrorIfUnsupportedAlterTableStmt(alterTableStatement); - /* * We check if there is a ADD FOREIGN CONSTRAINT command in sub commands list. - * If there is we assign referenced releation id to rightRelationId and we also + * If there is we assign referenced relation id to rightRelationId and we also * set skip_validation to true to prevent PostgreSQL to verify validity of the * foreign constraint in master. Validity will be checked in workers anyway. */ @@ -1454,190 +1462,55 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement) case AT_AddConstraint: { + Relation relation = NULL; + char distributionMethod; + Var *distributionColumn = NULL; + uint32 colocationId = 0; + + LOCKMODE lockmode = 0; + Oid leftRelationId = InvalidOid; + + lockmode = AlterTableGetLockLevel(alterTableStatement->cmds); + leftRelationId = AlterTableLookupRelation(alterTableStatement, lockmode); + + relation = relation_open(leftRelationId, ExclusiveLock); + + distributionMethod = PartitionMethod(leftRelationId); + distributionColumn = PartitionKey(leftRelationId); + + colocationId = TableColocationId(leftRelationId); + Constraint *constraint = (Constraint *) command->def; - LOCKMODE lockmode = AlterTableGetLockLevel(alterTableStatement->cmds); - Oid referencingTableId = InvalidOid; - Oid referencedTableId = InvalidOid; - Var *referencingTablePartitionColumn = NULL; - Var *referencedTablePartitionColumn = NULL; - ListCell *referencingTableAttr = NULL; - ListCell *referencedTableAttr = NULL; - bool foreignConstraintOnPartitionColumn = false; - /* we only allow adding foreign constraints with ALTER TABLE */ - if (constraint->contype != CONSTR_FOREIGN) + /* extra checks for alter table */ + if (constraint->contype == CONSTR_FOREIGN) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create constraint"), - errdetail("Citus cannot execute ADD CONSTRAINT " - "command other than ADD CONSTRAINT FOREIGN " - "KEY."))); - } - - /* we only allow foreign constraints if they are only subcommand */ - if (commandList->length > 1) - { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create foreign key constraint"), - errdetail("Citus cannot execute ADD CONSTRAINT " - "FOREIGN KEY command together with other " - "subcommands."), - errhint("You can issue each subcommand separately"))); - } - - referencingTableId = RangeVarGetRelid(alterTableStatement->relation, - lockmode, - alterTableStatement->missing_ok); - referencedTableId = RangeVarGetRelid(constraint->pktable, lockmode, - alterTableStatement->missing_ok); - - /* we do not support foreign keys for reference tables */ - if (PartitionMethod(referencingTableId) == DISTRIBUTE_BY_NONE || - PartitionMethod(referencedTableId) == DISTRIBUTE_BY_NONE) - { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create foreign key constraint"), - errdetail( - "Foreign key constraints are not allowed from or " - "to reference tables."))); - } - - /* - * ON DELETE SET NULL and ON DELETE SET DEFAULT is not supported. Because - * we do not want to set partition column to NULL or default value. - */ - if (constraint->fk_del_action == FKCONSTR_ACTION_SETNULL || - constraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT) - { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create foreign key constraint"), - errdetail("SET NULL or SET DEFAULT is not supported" - " in ON DELETE operation."))); - } - - /* - * ON UPDATE SET NULL, ON UPDATE SET DEFAULT and UPDATE CASCADE is not - * supported. Because we do not want to set partition column to NULL or - * default value. Also cascading update operation would require - * re-partitioning. Updating partition column value is not allowed anyway - * even outside of foreign key concept. - */ - if (constraint->fk_upd_action == FKCONSTR_ACTION_SETNULL || - constraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT || - constraint->fk_upd_action == FKCONSTR_ACTION_CASCADE) - { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create foreign key constraint"), - errdetail("SET NULL, SET DEFAULT or CASCADE is not" - " supported in ON UPDATE operation."))); - } - - /* - * We will use constraint name in each placement by extending it at - * workers. Therefore we require it to be exist. - */ - if (constraint->conname == NULL) - { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create foreign key constraint"), - errdetail("Creating foreign constraint without a " - "name on a distributed table is currently " - "not supported."))); - } - - /* to enforce foreign constraints, tables must be co-located */ - if (!TablesColocated(referencingTableId, referencedTableId)) - { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create foreign key constraint"), - errdetail("Foreign key constraint can only be created" - " on co-located tables."))); - } - - /* - * The following logic requires the referenced columns to exists in - * the statement. Otherwise, we cannot apply some of the checks. - */ - if (constraint->pk_attrs == NULL) - { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create foreign key constraint " - "because referenced column list is empty"), - errhint("Add column names to \"REFERENCES\" part of " - "the statement."))); - } - - /* - * Referencing column's list length should be equal to referenced columns - * list length. - */ - if (constraint->fk_attrs->length != constraint->pk_attrs->length) - { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create foreign key constraint"), - errdetail("Referencing column list and referenced " - "column list must be in same size."))); - } - - /* - * Partition column must exist in both referencing and referenced side - * of the foreign key constraint. They also must be in same ordinal. - */ - referencingTablePartitionColumn = PartitionKey(referencingTableId); - referencedTablePartitionColumn = PartitionKey(referencedTableId); - - /* - * We iterate over fk_attrs and pk_attrs together because partition - * column must be at the same place in both referencing and referenced - * side of the foreign key constraint - */ - forboth(referencingTableAttr, constraint->fk_attrs, - referencedTableAttr, constraint->pk_attrs) - { - char *referencingAttrName = strVal(lfirst(referencingTableAttr)); - char *referencedAttrName = strVal(lfirst(referencedTableAttr)); - AttrNumber referencingAttrNo = get_attnum(referencingTableId, - referencingAttrName); - AttrNumber referencedAttrNo = get_attnum(referencedTableId, - referencedAttrName); - - if (referencingTablePartitionColumn->varattno == referencingAttrNo && - referencedTablePartitionColumn->varattno == referencedAttrNo) + /* we only allow foreign constraints if they are only subcommand */ + if (commandList->length > 1) { - foreignConstraintOnPartitionColumn = true; + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot create foreign key constraint"), + errdetail("Citus cannot execute ADD CONSTRAINT " + "FOREIGN KEY command together with other " + "subcommands."), + errhint("You can issue each subcommand separately"))); + } + /* + * We will use constraint name in each placement by extending it at + * workers. Therefore we require it to be exist. + */ + if (constraint->conname == NULL) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot create foreign key constraint"), + errdetail("Creating foreign constraint without a " + "name on a distributed table is currently " + "not supported."))); } } - if (!foreignConstraintOnPartitionColumn) - { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create foreign key constraint"), - errdetail("Partition column must exist both " - "referencing and referenced side of the " - "foreign constraint statement and it must " - "be in the same ordinal in both sides."))); - } - - /* - * We do not allow to create foreign constraints if shard replication - * factor is greater than 1. Because in our current design, multiple - * replicas may cause locking problems and inconsistent shard contents. - */ - if (!SingleReplicatedTable(referencingTableId) || - !SingleReplicatedTable(referencedTableId)) - { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create foreign key constraint"), - errdetail("Citus Community Edition currently " - "supports foreign key constraints only for " - "\"citus.shard_replication_factor = 1\"."), - errhint("Please change " - "\"citus.shard_replication_factor to 1\". To " - "learn more about using foreign keys with " - "other replication factors, please contact" - " us at " - "https://citusdata.com/about/contact_us."))); - } + ErrorIfNotSupportedConstraint(relation, distributionMethod, + distributionColumn, colocationId); break; } diff --git a/src/include/distributed/create_distributed_table.h b/src/include/distributed/create_distributed_table.h new file mode 100644 index 000000000..02b76fb93 --- /dev/null +++ b/src/include/distributed/create_distributed_table.h @@ -0,0 +1,16 @@ +/* + * create_distributed_table.h + * + * Created on: Mar 17, 2017 + * Author: velioglub + */ + +#ifndef SRC_INCLUDE_DISTRIBUTED_CREATE_DISTRIBUTED_TABLE_H_ +#define SRC_INCLUDE_DISTRIBUTED_CREATE_DISTRIBUTED_TABLE_H_ + +extern void ErrorIfNotSupportedConstraint(Relation relation, + char distributionMethod, + Var *distributionColumn, + uint32 colocationId); + +#endif /* SRC_INCLUDE_DISTRIBUTED_CREATE_DISTRIBUTED_TABLE_H_ */