mirror of https://github.com/citusdata/citus.git
Merge pull request #1292 from citusdata/alter_add_constraint_m
Alter Table Add Constraintpull/1229/head
commit
701aaccd9c
|
@ -20,9 +20,7 @@
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "catalog/dependency.h"
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/index.h"
|
#include "catalog/index.h"
|
||||||
#include "catalog/indexing.h"
|
|
||||||
#include "catalog/pg_am.h"
|
#include "catalog/pg_am.h"
|
||||||
#include "catalog/pg_constraint.h"
|
|
||||||
#if (PG_VERSION_NUM >= 90600)
|
#if (PG_VERSION_NUM >= 90600)
|
||||||
#include "catalog/pg_constraint_fn.h"
|
#include "catalog/pg_constraint_fn.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -30,7 +28,6 @@
|
||||||
#include "catalog/pg_extension.h"
|
#include "catalog/pg_extension.h"
|
||||||
#include "catalog/pg_opclass.h"
|
#include "catalog/pg_opclass.h"
|
||||||
#include "catalog/pg_trigger.h"
|
#include "catalog/pg_trigger.h"
|
||||||
#include "catalog/pg_type.h"
|
|
||||||
#include "commands/defrem.h"
|
#include "commands/defrem.h"
|
||||||
#include "commands/extension.h"
|
#include "commands/extension.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
|
@ -42,6 +39,7 @@
|
||||||
#include "distributed/metadata_sync.h"
|
#include "distributed/metadata_sync.h"
|
||||||
#include "distributed/multi_copy.h"
|
#include "distributed/multi_copy.h"
|
||||||
#include "distributed/multi_logical_planner.h"
|
#include "distributed/multi_logical_planner.h"
|
||||||
|
#include "distributed/multi_utility.h"
|
||||||
#include "distributed/pg_dist_colocation.h"
|
#include "distributed/pg_dist_colocation.h"
|
||||||
#include "distributed/pg_dist_partition.h"
|
#include "distributed/pg_dist_partition.h"
|
||||||
#include "distributed/reference_table_utils.h"
|
#include "distributed/reference_table_utils.h"
|
||||||
|
@ -59,7 +57,6 @@
|
||||||
#include "tcop/pquery.h"
|
#include "tcop/pquery.h"
|
||||||
#include "tcop/tcopprot.h"
|
#include "tcop/tcopprot.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/fmgroids.h"
|
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
|
@ -81,12 +78,6 @@ static char LookupDistributionMethod(Oid distributionMethodOid);
|
||||||
static Oid SupportFunctionForColumn(Var *partitionColumn, Oid accessMethodId,
|
static Oid SupportFunctionForColumn(Var *partitionColumn, Oid accessMethodId,
|
||||||
int16 supportFunctionNumber);
|
int16 supportFunctionNumber);
|
||||||
static bool LocalTableEmpty(Oid tableId);
|
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,
|
|
||||||
uint32 colocationId);
|
|
||||||
static void CreateHashDistributedTable(Oid relationId, char *distributionColumnName,
|
static void CreateHashDistributedTable(Oid relationId, char *distributionColumnName,
|
||||||
char *colocateWithTableName,
|
char *colocateWithTableName,
|
||||||
int shardCount, int replicationFactor);
|
int shardCount, int replicationFactor);
|
||||||
|
@ -401,8 +392,8 @@ ConvertToDistributedTable(Oid relationId, char *distributionColumnName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorIfNotSupportedConstraint(relation, distributionMethod, distributionColumn,
|
ErrorIfUnsupportedConstraint(relation, distributionMethod, distributionColumn,
|
||||||
colocationId);
|
colocationId);
|
||||||
|
|
||||||
InsertIntoPgDistPartition(relationId, distributionMethod, distributionColumn,
|
InsertIntoPgDistPartition(relationId, distributionMethod, distributionColumn,
|
||||||
colocationId, replicationModel);
|
colocationId, replicationModel);
|
||||||
|
@ -420,348 +411,6 @@ ConvertToDistributedTable(Oid relationId, char *distributionColumnName,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ErrorIfNotSupportedConstraint run checks related to unique index / exclude
|
|
||||||
* constraints.
|
|
||||||
*
|
|
||||||
* The function skips the uniqeness checks for reference tables (i.e., distribution
|
|
||||||
* method is 'none').
|
|
||||||
*
|
|
||||||
* Forbid UNIQUE, PRIMARY KEY, or EXCLUDE constraints on append partitioned
|
|
||||||
* tables, since currently there is no way of enforcing uniqueness for
|
|
||||||
* overlapping shards.
|
|
||||||
*
|
|
||||||
* Similarly, do not allow such constraints if they do not include partition
|
|
||||||
* column. This check is important for two reasons:
|
|
||||||
* i. First, currently Citus does not enforce uniqueness constraint on multiple
|
|
||||||
* shards.
|
|
||||||
* ii. Second, INSERT INTO .. ON CONFLICT (i.e., UPSERT) queries can be executed
|
|
||||||
* with no further check for constraints.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
ErrorIfNotSupportedConstraint(Relation relation, char distributionMethod,
|
|
||||||
Var *distributionColumn, uint32 colocationId)
|
|
||||||
{
|
|
||||||
char *relationName = NULL;
|
|
||||||
List *indexOidList = NULL;
|
|
||||||
ListCell *indexOidCell = NULL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We first perform check for foreign constraints. It is important to do this check
|
|
||||||
* before next check, because other types of constraints are allowed on reference
|
|
||||||
* tables and we return early for those constraints thanks to next check. Therefore,
|
|
||||||
* for reference tables, we first check for foreing constraints and if they are OK,
|
|
||||||
* we do not error out for other types of constraints.
|
|
||||||
*/
|
|
||||||
ErrorIfNotSupportedForeignConstraint(relation, distributionMethod, distributionColumn,
|
|
||||||
colocationId);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Citus supports any kind of uniqueness constraints for reference tables
|
|
||||||
* given that they only consist of a single shard and we can simply rely on
|
|
||||||
* Postgres.
|
|
||||||
*/
|
|
||||||
if (distributionMethod == DISTRIBUTE_BY_NONE)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
relationName = RelationGetRelationName(relation);
|
|
||||||
indexOidList = RelationGetIndexList(relation);
|
|
||||||
|
|
||||||
foreach(indexOidCell, indexOidList)
|
|
||||||
{
|
|
||||||
Oid indexOid = lfirst_oid(indexOidCell);
|
|
||||||
Relation indexDesc = index_open(indexOid, RowExclusiveLock);
|
|
||||||
IndexInfo *indexInfo = NULL;
|
|
||||||
AttrNumber *attributeNumberArray = NULL;
|
|
||||||
bool hasDistributionColumn = false;
|
|
||||||
int attributeCount = 0;
|
|
||||||
int attributeIndex = 0;
|
|
||||||
|
|
||||||
/* extract index key information from the index's pg_index info */
|
|
||||||
indexInfo = BuildIndexInfo(indexDesc);
|
|
||||||
|
|
||||||
/* only check unique indexes and exclusion constraints. */
|
|
||||||
if (indexInfo->ii_Unique == false && indexInfo->ii_ExclusionOps == NULL)
|
|
||||||
{
|
|
||||||
index_close(indexDesc, NoLock);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Citus cannot enforce uniqueness/exclusion constraints with overlapping shards.
|
|
||||||
* Thus, emit a warning for unique indexes and exclusion constraints on
|
|
||||||
* append partitioned tables.
|
|
||||||
*/
|
|
||||||
if (distributionMethod == DISTRIBUTE_BY_APPEND)
|
|
||||||
{
|
|
||||||
ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("table \"%s\" has a UNIQUE or EXCLUDE constraint",
|
|
||||||
relationName),
|
|
||||||
errdetail("UNIQUE constraints, EXCLUDE constraints, "
|
|
||||||
"and PRIMARY KEYs on "
|
|
||||||
"append-partitioned tables cannot be enforced."),
|
|
||||||
errhint("Consider using hash partitioning.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
attributeCount = indexInfo->ii_NumIndexAttrs;
|
|
||||||
attributeNumberArray = indexInfo->ii_KeyAttrNumbers;
|
|
||||||
|
|
||||||
for (attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++)
|
|
||||||
{
|
|
||||||
AttrNumber attributeNumber = attributeNumberArray[attributeIndex];
|
|
||||||
bool uniqueConstraint = false;
|
|
||||||
bool exclusionConstraintWithEquality = false;
|
|
||||||
|
|
||||||
if (distributionColumn->varattno != attributeNumber)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
uniqueConstraint = indexInfo->ii_Unique;
|
|
||||||
exclusionConstraintWithEquality = (indexInfo->ii_ExclusionOps != NULL &&
|
|
||||||
OperatorImplementsEquality(
|
|
||||||
indexInfo->ii_ExclusionOps[
|
|
||||||
attributeIndex]));
|
|
||||||
|
|
||||||
if (uniqueConstraint || exclusionConstraintWithEquality)
|
|
||||||
{
|
|
||||||
hasDistributionColumn = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasDistributionColumn)
|
|
||||||
{
|
|
||||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("cannot distribute relation: \"%s\"",
|
|
||||||
relationName),
|
|
||||||
errdetail("Distributed relations cannot have UNIQUE, "
|
|
||||||
"EXCLUDE, or PRIMARY KEY constraints that do not "
|
|
||||||
"include the partition column (with an equality "
|
|
||||||
"operator if EXCLUDE).")));
|
|
||||||
}
|
|
||||||
|
|
||||||
index_close(indexDesc, NoLock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ErrorIfNotSupportedForeignConstraint runs checks related to foreign constraints and
|
|
||||||
* errors out if it is not possible to create one of the foreign constraint in distributed
|
|
||||||
* environment.
|
|
||||||
*
|
|
||||||
* To support foreign constraints, we require that;
|
|
||||||
* - Referencing and referenced tables are hash distributed.
|
|
||||||
* - Referencing and referenced tables are co-located.
|
|
||||||
* - Foreign constraint is defined over distribution column.
|
|
||||||
* - ON DELETE/UPDATE SET NULL, ON DELETE/UPDATE SET DEFAULT and ON UPDATE CASCADE options
|
|
||||||
* are not used.
|
|
||||||
* - Replication factors of referencing and referenced table are 1.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
ErrorIfNotSupportedForeignConstraint(Relation relation, char distributionMethod,
|
|
||||||
Var *distributionColumn, uint32 colocationId)
|
|
||||||
{
|
|
||||||
Relation pgConstraint = NULL;
|
|
||||||
SysScanDesc scanDescriptor = NULL;
|
|
||||||
ScanKeyData scanKey[1];
|
|
||||||
int scanKeyCount = 1;
|
|
||||||
HeapTuple heapTuple = NULL;
|
|
||||||
|
|
||||||
Oid referencedTableId = InvalidOid;
|
|
||||||
uint32 referencedTableColocationId = INVALID_COLOCATION_ID;
|
|
||||||
Var *referencedTablePartitionColumn = NULL;
|
|
||||||
|
|
||||||
Datum referencingColumnsDatum;
|
|
||||||
Datum *referencingColumnArray;
|
|
||||||
int referencingColumnCount = 0;
|
|
||||||
Datum referencedColumnsDatum;
|
|
||||||
Datum *referencedColumnArray;
|
|
||||||
int referencedColumnCount = 0;
|
|
||||||
bool isNull = false;
|
|
||||||
int attrIdx = 0;
|
|
||||||
bool foreignConstraintOnPartitionColumn = false;
|
|
||||||
bool selfReferencingTable = false;
|
|
||||||
|
|
||||||
pgConstraint = heap_open(ConstraintRelationId, AccessShareLock);
|
|
||||||
ScanKeyInit(&scanKey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ,
|
|
||||||
relation->rd_id);
|
|
||||||
scanDescriptor = systable_beginscan(pgConstraint, ConstraintRelidIndexId, true, NULL,
|
|
||||||
scanKeyCount, scanKey);
|
|
||||||
|
|
||||||
heapTuple = systable_getnext(scanDescriptor);
|
|
||||||
while (HeapTupleIsValid(heapTuple))
|
|
||||||
{
|
|
||||||
Form_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);
|
|
||||||
|
|
||||||
if (constraintForm->contype != CONSTRAINT_FOREIGN)
|
|
||||||
{
|
|
||||||
heapTuple = systable_getnext(scanDescriptor);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
referencedTableId = constraintForm->confrelid;
|
|
||||||
selfReferencingTable = relation->rd_id == referencedTableId;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We do not support foreign keys for reference tables. Here we skip the second
|
|
||||||
* part of check if the table is a self referencing table because;
|
|
||||||
* - PartitionMethod only works for distributed tables and this table is not
|
|
||||||
* distributed yet.
|
|
||||||
* - Since referencing and referenced tables are same, it is OK to not checking
|
|
||||||
* distribution method twice.
|
|
||||||
*/
|
|
||||||
if (distributionMethod == DISTRIBUTE_BY_NONE ||
|
|
||||||
(!selfReferencingTable &&
|
|
||||||
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 (constraintForm->confdeltype == FKCONSTR_ACTION_SETNULL ||
|
|
||||||
constraintForm->confdeltype == 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 (constraintForm->confupdtype == FKCONSTR_ACTION_SETNULL ||
|
|
||||||
constraintForm->confupdtype == FKCONSTR_ACTION_SETDEFAULT ||
|
|
||||||
constraintForm->confupdtype == 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.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Some checks are not meaningful if foreign key references the table itself.
|
|
||||||
* Therefore we will skip those checks.
|
|
||||||
*/
|
|
||||||
if (!selfReferencingTable)
|
|
||||||
{
|
|
||||||
if (!IsDistributedTable(referencedTableId))
|
|
||||||
{
|
|
||||||
ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
|
|
||||||
errmsg("cannot create foreign key constraint"),
|
|
||||||
errdetail("Referenced table must be a distributed "
|
|
||||||
"table.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* to enforce foreign constraints, tables must be co-located */
|
|
||||||
referencedTableColocationId = TableColocationId(referencedTableId);
|
|
||||||
if (relation->rd_id != referencedTableId &&
|
|
||||||
(colocationId == INVALID_COLOCATION_ID ||
|
|
||||||
colocationId != referencedTableColocationId))
|
|
||||||
{
|
|
||||||
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.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Partition column must exist in both referencing and referenced side of the
|
|
||||||
* foreign key constraint. They also must be in same ordinal.
|
|
||||||
*/
|
|
||||||
referencedTablePartitionColumn = PartitionKey(referencedTableId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Partition column must exist in both referencing and referenced side of the
|
|
||||||
* foreign key constraint. They also must be in same ordinal.
|
|
||||||
*/
|
|
||||||
referencedTablePartitionColumn = distributionColumn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Column attributes are not available in Form_pg_constraint, therefore we need
|
|
||||||
* to find them in the system catalog. After finding them, we iterate over column
|
|
||||||
* attributes together because partition column must be at the same place in both
|
|
||||||
* referencing and referenced side of the foreign key constraint
|
|
||||||
*/
|
|
||||||
referencingColumnsDatum = SysCacheGetAttr(CONSTROID, heapTuple,
|
|
||||||
Anum_pg_constraint_conkey, &isNull);
|
|
||||||
referencedColumnsDatum = SysCacheGetAttr(CONSTROID, heapTuple,
|
|
||||||
Anum_pg_constraint_confkey, &isNull);
|
|
||||||
|
|
||||||
deconstruct_array(DatumGetArrayTypeP(referencingColumnsDatum), INT2OID, 2, true,
|
|
||||||
's', &referencingColumnArray, NULL, &referencingColumnCount);
|
|
||||||
deconstruct_array(DatumGetArrayTypeP(referencedColumnsDatum), INT2OID, 2, true,
|
|
||||||
's', &referencedColumnArray, NULL, &referencedColumnCount);
|
|
||||||
|
|
||||||
Assert(referencingColumnCount == referencedColumnCount);
|
|
||||||
|
|
||||||
for (attrIdx = 0; attrIdx < referencingColumnCount; ++attrIdx)
|
|
||||||
{
|
|
||||||
AttrNumber referencingAttrNo = DatumGetInt16(referencingColumnArray[attrIdx]);
|
|
||||||
AttrNumber referencedAttrNo = DatumGetInt16(referencedColumnArray[attrIdx]);
|
|
||||||
|
|
||||||
if (distributionColumn->varattno == referencingAttrNo &&
|
|
||||||
referencedTablePartitionColumn->varattno == referencedAttrNo)
|
|
||||||
{
|
|
||||||
foreignConstraintOnPartitionColumn = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (ShardReplicationFactor > 1 || (referencedTableId != relation->rd_id &&
|
|
||||||
!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.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
heapTuple = systable_getnext(scanDescriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clean up scan and close system catalog */
|
|
||||||
systable_endscan(scanDescriptor);
|
|
||||||
heap_close(pgConstraint, AccessShareLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* LookupDistributionMethod maps the oids of citus.distribution_type enum
|
* LookupDistributionMethod maps the oids of citus.distribution_type enum
|
||||||
* values to pg_dist_partition.partmethod values.
|
* values to pg_dist_partition.partmethod values.
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
#include "catalog/pg_attribute.h"
|
#include "catalog/pg_attribute.h"
|
||||||
#include "catalog/pg_class.h"
|
#include "catalog/pg_class.h"
|
||||||
#include "citus_version.h"
|
#include "citus_version.h"
|
||||||
|
#include "catalog/pg_constraint.h"
|
||||||
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/defrem.h"
|
#include "commands/defrem.h"
|
||||||
#include "commands/tablecmds.h"
|
#include "commands/tablecmds.h"
|
||||||
#include "commands/prepare.h"
|
#include "commands/prepare.h"
|
||||||
|
@ -70,6 +72,7 @@
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/elog.h"
|
#include "utils/elog.h"
|
||||||
#include "utils/errcodes.h"
|
#include "utils/errcodes.h"
|
||||||
|
#include "utils/fmgroids.h"
|
||||||
#include "utils/guc.h"
|
#include "utils/guc.h"
|
||||||
#include "utils/hsearch.h"
|
#include "utils/hsearch.h"
|
||||||
#include "utils/inval.h"
|
#include "utils/inval.h"
|
||||||
|
@ -134,6 +137,11 @@ static void ErrorIfDistributedAlterSeqOwnedBy(AlterSeqStmt *alterSeqStmt);
|
||||||
static void ErrorIfUnsupportedTruncateStmt(TruncateStmt *truncateStatement);
|
static void ErrorIfUnsupportedTruncateStmt(TruncateStmt *truncateStatement);
|
||||||
static bool OptionsSpecifyOwnedBy(List *optionList, Oid *ownedByTableId);
|
static bool OptionsSpecifyOwnedBy(List *optionList, Oid *ownedByTableId);
|
||||||
static void ErrorIfUnsupportedRenameStmt(RenameStmt *renameStmt);
|
static void ErrorIfUnsupportedRenameStmt(RenameStmt *renameStmt);
|
||||||
|
static void ErrorIfUnsupportedAlterAddConstraintStmt(AlterTableStmt *alterTableStatement);
|
||||||
|
static void ErrorIfUnsupportedForeignConstraint(Relation relation,
|
||||||
|
char distributionMethod,
|
||||||
|
Var *distributionColumn,
|
||||||
|
uint32 colocationId);
|
||||||
|
|
||||||
/* Local functions forward declarations for helper functions */
|
/* Local functions forward declarations for helper functions */
|
||||||
static char * ExtractNewExtensionVersion(Node *parsetree);
|
static char * ExtractNewExtensionVersion(Node *parsetree);
|
||||||
|
@ -388,6 +396,33 @@ multi_ProcessUtility(Node *parsetree,
|
||||||
{
|
{
|
||||||
ListCell *ddlJobCell = NULL;
|
ListCell *ddlJobCell = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At this point, ALTER TABLE command has already run on the master, so we
|
||||||
|
* are checking constraints over the table with constraints already defined
|
||||||
|
* (to make the constraint check process same for ALTER TABLE and CREATE
|
||||||
|
* TABLE). If constraints do not fulfill the rules we defined, they will
|
||||||
|
* be removed and the table will return back to the state before the ALTER
|
||||||
|
* TABLE command.
|
||||||
|
*/
|
||||||
|
if (IsA(parsetree, AlterTableStmt))
|
||||||
|
{
|
||||||
|
AlterTableStmt *alterTableStatement = (AlterTableStmt *) parsetree;
|
||||||
|
List *commandList = alterTableStatement->cmds;
|
||||||
|
ListCell *commandCell = NULL;
|
||||||
|
|
||||||
|
foreach(commandCell, commandList)
|
||||||
|
{
|
||||||
|
AlterTableCmd *command = (AlterTableCmd *) lfirst(commandCell);
|
||||||
|
AlterTableType alterTableType = command->subtype;
|
||||||
|
|
||||||
|
if (alterTableType == AT_AddConstraint)
|
||||||
|
{
|
||||||
|
Assert(list_length(commandList) == 1);
|
||||||
|
ErrorIfUnsupportedAlterAddConstraintStmt(alterTableStatement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach(ddlJobCell, ddlJobs)
|
foreach(ddlJobCell, ddlJobs)
|
||||||
{
|
{
|
||||||
DDLJob *ddlJob = (DDLJob *) lfirst(ddlJobCell);
|
DDLJob *ddlJob = (DDLJob *) lfirst(ddlJobCell);
|
||||||
|
@ -938,7 +973,7 @@ PlanAlterTableStmt(AlterTableStmt *alterTableStatement, const char *alterTableCo
|
||||||
* only subcommand of ALTER TABLE. It was already checked in
|
* only subcommand of ALTER TABLE. It was already checked in
|
||||||
* ErrorIfUnsupportedAlterTableStmt.
|
* ErrorIfUnsupportedAlterTableStmt.
|
||||||
*/
|
*/
|
||||||
Assert(commandList->length <= 1);
|
Assert(list_length(commandList) <= 1);
|
||||||
|
|
||||||
rightRelationId = RangeVarGetRelid(constraint->pktable, lockmode,
|
rightRelationId = RangeVarGetRelid(constraint->pktable, lockmode,
|
||||||
alterTableStatement->missing_ok);
|
alterTableStatement->missing_ok);
|
||||||
|
@ -1584,7 +1619,7 @@ ErrorIfUnsupportedDropIndexStmt(DropStmt *dropIndexStatement)
|
||||||
* ALTER TABLE ALTER COLUMN SET DATA TYPE
|
* ALTER TABLE ALTER COLUMN SET DATA TYPE
|
||||||
* ALTER TABLE SET|DROP NOT NULL
|
* ALTER TABLE SET|DROP NOT NULL
|
||||||
* ALTER TABLE SET|DROP DEFAULT
|
* ALTER TABLE SET|DROP DEFAULT
|
||||||
* ALTER TABLE ADD|DROP CONSTRAINT FOREIGN
|
* ALTER TABLE ADD|DROP CONSTRAINT
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement)
|
ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement)
|
||||||
|
@ -1635,7 +1670,6 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement)
|
||||||
case AT_DropColumn:
|
case AT_DropColumn:
|
||||||
case AT_ColumnDefault:
|
case AT_ColumnDefault:
|
||||||
case AT_AlterColumnType:
|
case AT_AlterColumnType:
|
||||||
case AT_SetNotNull:
|
|
||||||
case AT_DropNotNull:
|
case AT_DropNotNull:
|
||||||
{
|
{
|
||||||
/* error out if the alter table command is on the partition column */
|
/* error out if the alter table command is on the partition column */
|
||||||
|
@ -1675,83 +1709,16 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement)
|
||||||
case AT_AddConstraint:
|
case AT_AddConstraint:
|
||||||
{
|
{
|
||||||
Constraint *constraint = (Constraint *) command->def;
|
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 */
|
/* we only allow constraints if they are only subcommand */
|
||||||
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)
|
if (commandList->length > 1)
|
||||||
{
|
{
|
||||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("cannot create foreign key constraint"),
|
errmsg("cannot execute ADD CONSTRAINT command with "
|
||||||
errdetail("Citus cannot execute ADD CONSTRAINT "
|
"other subcommands"),
|
||||||
"FOREIGN KEY command together with other "
|
|
||||||
"subcommands."),
|
|
||||||
errhint("You can issue each subcommand separately")));
|
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
|
* We will use constraint name in each placement by extending it at
|
||||||
* workers. Therefore we require it to be exist.
|
* workers. Therefore we require it to be exist.
|
||||||
|
@ -1759,114 +1726,23 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement)
|
||||||
if (constraint->conname == NULL)
|
if (constraint->conname == NULL)
|
||||||
{
|
{
|
||||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("cannot create foreign key constraint"),
|
errmsg("cannot create constraint without a name on a "
|
||||||
errdetail("Creating foreign constraint without a "
|
"distributed table")));
|
||||||
"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)
|
|
||||||
{
|
|
||||||
foreignConstraintOnPartitionColumn = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case AT_SetNotNull:
|
||||||
case AT_DropConstraint:
|
case AT_DropConstraint:
|
||||||
case AT_EnableTrigAll:
|
case AT_EnableTrigAll:
|
||||||
case AT_DisableTrigAll:
|
case AT_DisableTrigAll:
|
||||||
{
|
{
|
||||||
/* we will not perform any special checks for these ALTER TABLE types */
|
/*
|
||||||
|
* We will not perform any special check for ALTER TABLE DROP CONSTRAINT
|
||||||
|
* , ALTER TABLE .. ALTER COLUMN .. SET NOT NULL and ALTER TABLE ENABLE/
|
||||||
|
* DISABLE TRIGGER ALL
|
||||||
|
*/
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1875,14 +1751,394 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement)
|
||||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("alter table command is currently unsupported"),
|
errmsg("alter table command is currently unsupported"),
|
||||||
errdetail("Only ADD|DROP COLUMN, SET|DROP NOT NULL,"
|
errdetail("Only ADD|DROP COLUMN, SET|DROP NOT NULL,"
|
||||||
" SET|DROP DEFAULT, ADD|DROP CONSTRAINT FOREIGN"
|
" SET|DROP DEFAULT, ADD|DROP CONSTRAINT and "
|
||||||
" KEY and TYPE subcommands are supported.")));
|
"TYPE subcommands are supported.")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ErrorIfUnsopprtedAlterAddConstraintStmt runs the constraint checks on distributed
|
||||||
|
* table using the same logic with create_distributed_table.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ErrorIfUnsupportedAlterAddConstraintStmt(AlterTableStmt *alterTableStatement)
|
||||||
|
{
|
||||||
|
LOCKMODE lockmode = AlterTableGetLockLevel(alterTableStatement->cmds);
|
||||||
|
Oid relationId = AlterTableLookupRelation(alterTableStatement, lockmode);
|
||||||
|
char distributionMethod = PartitionMethod(relationId);
|
||||||
|
Var *distributionColumn = PartitionKey(relationId);
|
||||||
|
uint32 colocationId = TableColocationId(relationId);
|
||||||
|
Relation relation = relation_open(relationId, ExclusiveLock);
|
||||||
|
|
||||||
|
ErrorIfUnsupportedConstraint(relation, distributionMethod, distributionColumn,
|
||||||
|
colocationId);
|
||||||
|
relation_close(relation, NoLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ErrorIfUnsupportedConstraint run checks related to unique index / exclude
|
||||||
|
* constraints.
|
||||||
|
*
|
||||||
|
* The function skips the uniqeness checks for reference tables (i.e., distribution
|
||||||
|
* method is 'none').
|
||||||
|
*
|
||||||
|
* Forbid UNIQUE, PRIMARY KEY, or EXCLUDE constraints on append partitioned
|
||||||
|
* tables, since currently there is no way of enforcing uniqueness for
|
||||||
|
* overlapping shards.
|
||||||
|
*
|
||||||
|
* Similarly, do not allow such constraints if they do not include partition
|
||||||
|
* column. This check is important for two reasons:
|
||||||
|
* i. First, currently Citus does not enforce uniqueness constraint on multiple
|
||||||
|
* shards.
|
||||||
|
* ii. Second, INSERT INTO .. ON CONFLICT (i.e., UPSERT) queries can be executed
|
||||||
|
* with no further check for constraints.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ErrorIfUnsupportedConstraint(Relation relation, char distributionMethod,
|
||||||
|
Var *distributionColumn, uint32 colocationId)
|
||||||
|
{
|
||||||
|
char *relationName = NULL;
|
||||||
|
List *indexOidList = NULL;
|
||||||
|
ListCell *indexOidCell = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We first perform check for foreign constraints. It is important to do this check
|
||||||
|
* before next check, because other types of constraints are allowed on reference
|
||||||
|
* tables and we return early for those constraints thanks to next check. Therefore,
|
||||||
|
* for reference tables, we first check for foreing constraints and if they are OK,
|
||||||
|
* we do not error out for other types of constraints.
|
||||||
|
*/
|
||||||
|
ErrorIfUnsupportedForeignConstraint(relation, distributionMethod, distributionColumn,
|
||||||
|
colocationId);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Citus supports any kind of uniqueness constraints for reference tables
|
||||||
|
* given that they only consist of a single shard and we can simply rely on
|
||||||
|
* Postgres.
|
||||||
|
*/
|
||||||
|
if (distributionMethod == DISTRIBUTE_BY_NONE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
relationName = RelationGetRelationName(relation);
|
||||||
|
indexOidList = RelationGetIndexList(relation);
|
||||||
|
|
||||||
|
foreach(indexOidCell, indexOidList)
|
||||||
|
{
|
||||||
|
Oid indexOid = lfirst_oid(indexOidCell);
|
||||||
|
Relation indexDesc = index_open(indexOid, RowExclusiveLock);
|
||||||
|
IndexInfo *indexInfo = NULL;
|
||||||
|
AttrNumber *attributeNumberArray = NULL;
|
||||||
|
bool hasDistributionColumn = false;
|
||||||
|
int attributeCount = 0;
|
||||||
|
int attributeIndex = 0;
|
||||||
|
|
||||||
|
/* extract index key information from the index's pg_index info */
|
||||||
|
indexInfo = BuildIndexInfo(indexDesc);
|
||||||
|
|
||||||
|
/* only check unique indexes and exclusion constraints. */
|
||||||
|
if (indexInfo->ii_Unique == false && indexInfo->ii_ExclusionOps == NULL)
|
||||||
|
{
|
||||||
|
index_close(indexDesc, NoLock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Citus cannot enforce uniqueness/exclusion constraints with overlapping shards.
|
||||||
|
* Thus, emit a warning for unique indexes and exclusion constraints on
|
||||||
|
* append partitioned tables.
|
||||||
|
*/
|
||||||
|
if (distributionMethod == DISTRIBUTE_BY_APPEND)
|
||||||
|
{
|
||||||
|
ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("table \"%s\" has a UNIQUE or EXCLUDE constraint",
|
||||||
|
relationName),
|
||||||
|
errdetail("UNIQUE constraints, EXCLUDE constraints, "
|
||||||
|
"and PRIMARY KEYs on "
|
||||||
|
"append-partitioned tables cannot be enforced."),
|
||||||
|
errhint("Consider using hash partitioning.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeCount = indexInfo->ii_NumIndexAttrs;
|
||||||
|
attributeNumberArray = indexInfo->ii_KeyAttrNumbers;
|
||||||
|
|
||||||
|
for (attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++)
|
||||||
|
{
|
||||||
|
AttrNumber attributeNumber = attributeNumberArray[attributeIndex];
|
||||||
|
bool uniqueConstraint = false;
|
||||||
|
bool exclusionConstraintWithEquality = false;
|
||||||
|
|
||||||
|
if (distributionColumn->varattno != attributeNumber)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueConstraint = indexInfo->ii_Unique;
|
||||||
|
exclusionConstraintWithEquality = (indexInfo->ii_ExclusionOps != NULL &&
|
||||||
|
OperatorImplementsEquality(
|
||||||
|
indexInfo->ii_ExclusionOps[
|
||||||
|
attributeIndex]));
|
||||||
|
|
||||||
|
if (uniqueConstraint || exclusionConstraintWithEquality)
|
||||||
|
{
|
||||||
|
hasDistributionColumn = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasDistributionColumn)
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot create constraint on \"%s\"",
|
||||||
|
relationName),
|
||||||
|
errdetail("Distributed relations cannot have UNIQUE, "
|
||||||
|
"EXCLUDE, or PRIMARY KEY constraints that do not "
|
||||||
|
"include the partition column (with an equality "
|
||||||
|
"operator if EXCLUDE).")));
|
||||||
|
}
|
||||||
|
|
||||||
|
index_close(indexDesc, NoLock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ErrorIfUnsupportedForeignConstraint runs checks related to foreign constraints and
|
||||||
|
* errors out if it is not possible to create one of the foreign constraint in distributed
|
||||||
|
* environment.
|
||||||
|
*
|
||||||
|
* To support foreign constraints, we require that;
|
||||||
|
* - Referencing and referenced tables are hash distributed.
|
||||||
|
* - Referencing and referenced tables are co-located.
|
||||||
|
* - Foreign constraint is defined over distribution column.
|
||||||
|
* - ON DELETE/UPDATE SET NULL, ON DELETE/UPDATE SET DEFAULT and ON UPDATE CASCADE options
|
||||||
|
* are not used.
|
||||||
|
* - Replication factors of referencing and referenced table are 1.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ErrorIfUnsupportedForeignConstraint(Relation relation, char distributionMethod,
|
||||||
|
Var *distributionColumn, uint32 colocationId)
|
||||||
|
{
|
||||||
|
Relation pgConstraint = NULL;
|
||||||
|
SysScanDesc scanDescriptor = NULL;
|
||||||
|
ScanKeyData scanKey[1];
|
||||||
|
int scanKeyCount = 1;
|
||||||
|
HeapTuple heapTuple = NULL;
|
||||||
|
|
||||||
|
Oid referencingTableId = relation->rd_id;
|
||||||
|
Oid referencedTableId = InvalidOid;
|
||||||
|
uint32 referencedTableColocationId = INVALID_COLOCATION_ID;
|
||||||
|
Var *referencedTablePartitionColumn = NULL;
|
||||||
|
|
||||||
|
Datum referencingColumnsDatum;
|
||||||
|
Datum *referencingColumnArray;
|
||||||
|
int referencingColumnCount = 0;
|
||||||
|
Datum referencedColumnsDatum;
|
||||||
|
Datum *referencedColumnArray;
|
||||||
|
int referencedColumnCount = 0;
|
||||||
|
bool isNull = false;
|
||||||
|
int attrIdx = 0;
|
||||||
|
bool foreignConstraintOnPartitionColumn = false;
|
||||||
|
bool selfReferencingTable = false;
|
||||||
|
|
||||||
|
pgConstraint = heap_open(ConstraintRelationId, AccessShareLock);
|
||||||
|
ScanKeyInit(&scanKey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
relation->rd_id);
|
||||||
|
scanDescriptor = systable_beginscan(pgConstraint, ConstraintRelidIndexId, true, NULL,
|
||||||
|
scanKeyCount, scanKey);
|
||||||
|
|
||||||
|
heapTuple = systable_getnext(scanDescriptor);
|
||||||
|
while (HeapTupleIsValid(heapTuple))
|
||||||
|
{
|
||||||
|
Form_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);
|
||||||
|
bool singleReplicatedTable = true;
|
||||||
|
|
||||||
|
if (constraintForm->contype != CONSTRAINT_FOREIGN)
|
||||||
|
{
|
||||||
|
heapTuple = systable_getnext(scanDescriptor);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
referencedTableId = constraintForm->confrelid;
|
||||||
|
selfReferencingTable = referencingTableId == referencedTableId;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We do not support foreign keys for reference tables. Here we skip the second
|
||||||
|
* part of check if the table is a self referencing table because;
|
||||||
|
* - PartitionMethod only works for distributed tables and this table may not be
|
||||||
|
* distributed yet.
|
||||||
|
* - Since referencing and referenced tables are same, it is OK to not checking
|
||||||
|
* distribution method twice.
|
||||||
|
*/
|
||||||
|
if (distributionMethod == DISTRIBUTE_BY_NONE ||
|
||||||
|
(!selfReferencingTable &&
|
||||||
|
PartitionMethod(referencedTableId) == DISTRIBUTE_BY_NONE))
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot create foreign key constraint 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 (constraintForm->confdeltype == FKCONSTR_ACTION_SETNULL ||
|
||||||
|
constraintForm->confdeltype == 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 (constraintForm->confupdtype == FKCONSTR_ACTION_SETNULL ||
|
||||||
|
constraintForm->confupdtype == FKCONSTR_ACTION_SETDEFAULT ||
|
||||||
|
constraintForm->confupdtype == 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.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some checks are not meaningful if foreign key references the table itself.
|
||||||
|
* Therefore we will skip those checks.
|
||||||
|
*/
|
||||||
|
if (!selfReferencingTable)
|
||||||
|
{
|
||||||
|
if (!IsDistributedTable(referencedTableId))
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
|
||||||
|
errmsg("cannot create foreign key constraint"),
|
||||||
|
errdetail("Referenced table must be a distributed "
|
||||||
|
"table.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* to enforce foreign constraints, tables must be co-located */
|
||||||
|
referencedTableColocationId = TableColocationId(referencedTableId);
|
||||||
|
if (colocationId == INVALID_COLOCATION_ID ||
|
||||||
|
colocationId != referencedTableColocationId)
|
||||||
|
{
|
||||||
|
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.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Partition column must exist in both referencing and referenced side of the
|
||||||
|
* foreign key constraint. They also must be in same ordinal.
|
||||||
|
*/
|
||||||
|
referencedTablePartitionColumn = PartitionKey(referencedTableId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Partition column must exist in both referencing and referenced side of the
|
||||||
|
* foreign key constraint. They also must be in same ordinal.
|
||||||
|
*/
|
||||||
|
referencedTablePartitionColumn = distributionColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Column attributes are not available in Form_pg_constraint, therefore we need
|
||||||
|
* to find them in the system catalog. After finding them, we iterate over column
|
||||||
|
* attributes together because partition column must be at the same place in both
|
||||||
|
* referencing and referenced side of the foreign key constraint
|
||||||
|
*/
|
||||||
|
referencingColumnsDatum = SysCacheGetAttr(CONSTROID, heapTuple,
|
||||||
|
Anum_pg_constraint_conkey, &isNull);
|
||||||
|
referencedColumnsDatum = SysCacheGetAttr(CONSTROID, heapTuple,
|
||||||
|
Anum_pg_constraint_confkey, &isNull);
|
||||||
|
|
||||||
|
deconstruct_array(DatumGetArrayTypeP(referencingColumnsDatum), INT2OID, 2, true,
|
||||||
|
's', &referencingColumnArray, NULL, &referencingColumnCount);
|
||||||
|
deconstruct_array(DatumGetArrayTypeP(referencedColumnsDatum), INT2OID, 2, true,
|
||||||
|
's', &referencedColumnArray, NULL, &referencedColumnCount);
|
||||||
|
|
||||||
|
Assert(referencingColumnCount == referencedColumnCount);
|
||||||
|
|
||||||
|
for (attrIdx = 0; attrIdx < referencingColumnCount; ++attrIdx)
|
||||||
|
{
|
||||||
|
AttrNumber referencingAttrNo = DatumGetInt16(referencingColumnArray[attrIdx]);
|
||||||
|
AttrNumber referencedAttrNo = DatumGetInt16(referencedColumnArray[attrIdx]);
|
||||||
|
|
||||||
|
if (distributionColumn->varattno == referencingAttrNo &&
|
||||||
|
referencedTablePartitionColumn->varattno == referencedAttrNo)
|
||||||
|
{
|
||||||
|
foreignConstraintOnPartitionColumn = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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. We don't check the referenced
|
||||||
|
* table, since referenced and referencing tables should be co-located and
|
||||||
|
* colocation check has been done above.
|
||||||
|
*/
|
||||||
|
if (IsDistributedTable(referencingTableId))
|
||||||
|
{
|
||||||
|
/* check whether ALTER TABLE command is applied over single replicated table */
|
||||||
|
if (!SingleReplicatedTable(referencingTableId))
|
||||||
|
{
|
||||||
|
singleReplicatedTable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* check whether creating single replicated table with foreign constraint */
|
||||||
|
if (ShardReplicationFactor > 1)
|
||||||
|
{
|
||||||
|
singleReplicatedTable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!singleReplicatedTable)
|
||||||
|
{
|
||||||
|
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.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
heapTuple = systable_getnext(scanDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clean up scan and close system catalog */
|
||||||
|
systable_endscan(scanDescriptor);
|
||||||
|
heap_close(pgConstraint, AccessShareLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ErrorIfUnsupportedSeqStmt errors out if the provided create sequence
|
* ErrorIfUnsupportedSeqStmt errors out if the provided create sequence
|
||||||
* statement specifies a distributed table in its OWNED BY clause.
|
* statement specifies a distributed table in its OWNED BY clause.
|
||||||
|
|
|
@ -33,6 +33,8 @@ extern void multi_ProcessUtility(Node *parsetree, const char *queryString,
|
||||||
ProcessUtilityContext context, ParamListInfo params,
|
ProcessUtilityContext context, ParamListInfo params,
|
||||||
DestReceiver *dest, char *completionTag);
|
DestReceiver *dest, char *completionTag);
|
||||||
extern List * PlanGrantStmt(GrantStmt *grantStmt);
|
extern List * PlanGrantStmt(GrantStmt *grantStmt);
|
||||||
|
extern void ErrorIfUnsupportedConstraint(Relation relation, char distributionMethod,
|
||||||
|
Var *distributionColumn, uint32 colocationId);
|
||||||
|
|
||||||
|
|
||||||
#endif /* MULTI_UTILITY_H */
|
#endif /* MULTI_UTILITY_H */
|
||||||
|
|
|
@ -0,0 +1,493 @@
|
||||||
|
--
|
||||||
|
-- MULTI_ALTER_TABLE_ADD_CONSTRAINTS
|
||||||
|
--
|
||||||
|
-- Test checks whether constraints of distributed tables can be adjusted using
|
||||||
|
-- the ALTER TABLE ... ADD CONSTRAINT ... command.
|
||||||
|
ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 1450000;
|
||||||
|
ALTER SEQUENCE pg_catalog.pg_dist_jobid_seq RESTART 1450000;
|
||||||
|
-- Check "PRIMARY KEY CONSTRAINT"
|
||||||
|
CREATE TABLE products (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
SELECT create_distributed_table('products', 'product_no');
|
||||||
|
create_distributed_table
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Can only add primary key constraint on distribution column (or group of columns
|
||||||
|
-- including distribution column)
|
||||||
|
-- Command below should error out since 'name' is not a distribution column
|
||||||
|
ALTER TABLE products ADD CONSTRAINT p_key PRIMARY KEY(name);
|
||||||
|
ERROR: cannot create constraint on "products"
|
||||||
|
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
||||||
|
ALTER TABLE products ADD CONSTRAINT p_key PRIMARY KEY(product_no);
|
||||||
|
NOTICE: using one-phase commit for distributed DDL commands
|
||||||
|
HINT: You can enable two-phase commit for extra safety with: SET citus.multi_shard_commit_protocol TO '2pc'
|
||||||
|
INSERT INTO products VALUES(1, 'product_1', 1);
|
||||||
|
-- Should error out, since we are trying to add a new row having a value on p_key column
|
||||||
|
-- conflicting with the existing row.
|
||||||
|
INSERT INTO products VALUES(1, 'product_1', 1);
|
||||||
|
ERROR: duplicate key value violates unique constraint "p_key_1450001"
|
||||||
|
DETAIL: Key (product_no)=(1) already exists.
|
||||||
|
CONTEXT: while executing command on localhost:57638
|
||||||
|
ALTER TABLE products DROP CONSTRAINT p_key;
|
||||||
|
INSERT INTO products VALUES(1, 'product_1', 1);
|
||||||
|
-- Can not create constraint since it conflicts with the existing data
|
||||||
|
ALTER TABLE products ADD CONSTRAINT p_key PRIMARY KEY(product_no);
|
||||||
|
ERROR: could not create unique index "p_key_1450001"
|
||||||
|
DETAIL: Key (product_no)=(1) is duplicated.
|
||||||
|
CONTEXT: while executing command on localhost:57637
|
||||||
|
DROP TABLE products;
|
||||||
|
-- Check "PRIMARY KEY CONSTRAINT" with reference table
|
||||||
|
CREATE TABLE products_ref (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
SELECT create_reference_table('products_ref');
|
||||||
|
create_reference_table
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Can add PRIMARY KEY to any column
|
||||||
|
ALTER TABLE products_ref ADD CONSTRAINT p_key PRIMARY KEY(name);
|
||||||
|
ALTER TABLE products_ref DROP CONSTRAINT p_key;
|
||||||
|
ALTER TABLE products_ref ADD CONSTRAINT p_key PRIMARY KEY(product_no);
|
||||||
|
INSERT INTO products_ref VALUES(1, 'product_1', 1);
|
||||||
|
-- Should error out, since we are trying to add new row having a value on p_key column
|
||||||
|
-- conflicting with the existing row.
|
||||||
|
INSERT INTO products_ref VALUES(1, 'product_1', 1);
|
||||||
|
ERROR: duplicate key value violates unique constraint "p_key_1450032"
|
||||||
|
DETAIL: Key (product_no)=(1) already exists.
|
||||||
|
CONTEXT: while executing command on localhost:57637
|
||||||
|
DROP TABLE products_ref;
|
||||||
|
-- Check "PRIMARY KEY CONSTRAINT" on append table
|
||||||
|
CREATE TABLE products_append (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
SELECT create_distributed_table('products_append', 'product_no', 'append');
|
||||||
|
create_distributed_table
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Can only add primary key constraint on distribution column (or group
|
||||||
|
-- of columns including distribution column)
|
||||||
|
-- Command below should error out since 'name' is not a distribution column
|
||||||
|
ALTER TABLE products_append ADD CONSTRAINT p_key_name PRIMARY KEY(name);
|
||||||
|
WARNING: table "products_append" has a UNIQUE or EXCLUDE constraint
|
||||||
|
DETAIL: UNIQUE constraints, EXCLUDE constraints, and PRIMARY KEYs on append-partitioned tables cannot be enforced.
|
||||||
|
HINT: Consider using hash partitioning.
|
||||||
|
ERROR: cannot create constraint on "products_append"
|
||||||
|
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
||||||
|
ALTER TABLE products_append ADD CONSTRAINT p_key PRIMARY KEY(product_no);
|
||||||
|
WARNING: table "products_append" has a UNIQUE or EXCLUDE constraint
|
||||||
|
DETAIL: UNIQUE constraints, EXCLUDE constraints, and PRIMARY KEYs on append-partitioned tables cannot be enforced.
|
||||||
|
HINT: Consider using hash partitioning.
|
||||||
|
--- Error out since first and third rows have the same product_no
|
||||||
|
\COPY products_append FROM STDIN DELIMITER AS ',';
|
||||||
|
ERROR: duplicate key value violates unique constraint "p_key_1450033"
|
||||||
|
DETAIL: Key (product_no)=(1) already exists.
|
||||||
|
DROP TABLE products_append;
|
||||||
|
-- Check "UNIQUE CONSTRAINT"
|
||||||
|
CREATE TABLE unique_test_table(id int, name varchar(20));
|
||||||
|
SELECT create_distributed_table('unique_test_table', 'id');
|
||||||
|
create_distributed_table
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Can only add unique constraint on distribution column (or group
|
||||||
|
-- of columns including distribution column)
|
||||||
|
-- Command below should error out since 'name' is not a distribution column
|
||||||
|
ALTER TABLE unique_test_table ADD CONSTRAINT unn_name UNIQUE(name);
|
||||||
|
ERROR: cannot create constraint on "unique_test_table"
|
||||||
|
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
||||||
|
ALTER TABLE unique_test_table ADD CONSTRAINT unn_id UNIQUE(id);
|
||||||
|
-- Error out, since table can not have two rows with same id.
|
||||||
|
INSERT INTO unique_test_table VALUES(1, 'Ahmet');
|
||||||
|
INSERT INTO unique_test_table VALUES(1, 'Mehmet');
|
||||||
|
ERROR: duplicate key value violates unique constraint "unn_id_1450035"
|
||||||
|
DETAIL: Key (id)=(1) already exists.
|
||||||
|
CONTEXT: while executing command on localhost:57638
|
||||||
|
ALTER TABLE unique_test_table DROP CONSTRAINT unn_id;
|
||||||
|
-- Insert row which will conflict with the next unique constraint command
|
||||||
|
INSERT INTO unique_test_table VALUES(1, 'Mehmet');
|
||||||
|
-- Can not create constraint since it conflicts with the existing data
|
||||||
|
ALTER TABLE unique_test_table ADD CONSTRAINT unn_id UNIQUE(id);
|
||||||
|
ERROR: could not create unique index "unn_id_1450035"
|
||||||
|
DETAIL: Key (id)=(1) is duplicated.
|
||||||
|
CONTEXT: while executing command on localhost:57637
|
||||||
|
-- Can create unique constraint over multiple columns which must include
|
||||||
|
-- distribution column
|
||||||
|
ALTER TABLE unique_test_table ADD CONSTRAINT unn_id_name UNIQUE(id, name);
|
||||||
|
-- Error out, since tables can not have two rows with same id and name.
|
||||||
|
INSERT INTO unique_test_table VALUES(1, 'Mehmet');
|
||||||
|
ERROR: duplicate key value violates unique constraint "unn_id_name_1450035"
|
||||||
|
DETAIL: Key (id, name)=(1, Mehmet) already exists.
|
||||||
|
CONTEXT: while executing command on localhost:57638
|
||||||
|
DROP TABLE unique_test_table;
|
||||||
|
-- Check "UNIQUE CONSTRAINT" with reference table
|
||||||
|
CREATE TABLE unique_test_table_ref(id int, name varchar(20));
|
||||||
|
SELECT create_reference_table('unique_test_table_ref');
|
||||||
|
create_reference_table
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- We can add unique constraint on any column with reference tables
|
||||||
|
ALTER TABLE unique_test_table_ref ADD CONSTRAINT unn_name UNIQUE(name);
|
||||||
|
ALTER TABLE unique_test_table_ref ADD CONSTRAINT unn_id UNIQUE(id);
|
||||||
|
-- Error out. Since the table can not have two rows with the same id.
|
||||||
|
INSERT INTO unique_test_table_ref VALUES(1, 'Ahmet');
|
||||||
|
INSERT INTO unique_test_table_ref VALUES(1, 'Mehmet');
|
||||||
|
ERROR: duplicate key value violates unique constraint "unn_id_1450066"
|
||||||
|
DETAIL: Key (id)=(1) already exists.
|
||||||
|
CONTEXT: while executing command on localhost:57637
|
||||||
|
-- We can add unique constraint with multiple columns
|
||||||
|
ALTER TABLE unique_test_table_ref DROP CONSTRAINT unn_id;
|
||||||
|
ALTER TABLE unique_test_table_ref ADD CONSTRAINT unn_id_name UNIQUE(id,name);
|
||||||
|
-- Error out, since two rows can not have the same id or name.
|
||||||
|
INSERT INTO unique_test_table_ref VALUES(1, 'Mehmet');
|
||||||
|
DROP TABLE unique_test_table_ref;
|
||||||
|
-- Check "UNIQUE CONSTRAINT" with append table
|
||||||
|
CREATE TABLE unique_test_table_append(id int, name varchar(20));
|
||||||
|
SELECT create_distributed_table('unique_test_table_append', 'id', 'append');
|
||||||
|
create_distributed_table
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Can only add unique constraint on distribution column (or group
|
||||||
|
-- of columns including distribution column)
|
||||||
|
-- Command below should error out since 'name' is not a distribution column
|
||||||
|
ALTER TABLE unique_test_table_append ADD CONSTRAINT unn_name UNIQUE(name);
|
||||||
|
WARNING: table "unique_test_table_append" has a UNIQUE or EXCLUDE constraint
|
||||||
|
DETAIL: UNIQUE constraints, EXCLUDE constraints, and PRIMARY KEYs on append-partitioned tables cannot be enforced.
|
||||||
|
HINT: Consider using hash partitioning.
|
||||||
|
ERROR: cannot create constraint on "unique_test_table_append"
|
||||||
|
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
||||||
|
ALTER TABLE unique_test_table_append ADD CONSTRAINT unn_id UNIQUE(id);
|
||||||
|
WARNING: table "unique_test_table_append" has a UNIQUE or EXCLUDE constraint
|
||||||
|
DETAIL: UNIQUE constraints, EXCLUDE constraints, and PRIMARY KEYs on append-partitioned tables cannot be enforced.
|
||||||
|
HINT: Consider using hash partitioning.
|
||||||
|
-- Error out. Table can not have two rows with the same id.
|
||||||
|
\COPY unique_test_table_append FROM STDIN DELIMITER AS ',';
|
||||||
|
ERROR: duplicate key value violates unique constraint "unn_id_1450067"
|
||||||
|
DETAIL: Key (id)=(1) already exists.
|
||||||
|
DROP TABLE unique_test_table_append;
|
||||||
|
-- Check "CHECK CONSTRAINT"
|
||||||
|
CREATE TABLE products (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric,
|
||||||
|
discounted_price numeric
|
||||||
|
);
|
||||||
|
SELECT create_distributed_table('products', 'product_no');
|
||||||
|
create_distributed_table
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Can add column and table check constraints
|
||||||
|
ALTER TABLE products ADD CONSTRAINT p_check CHECK(price > 0);
|
||||||
|
ALTER TABLE products ADD CONSTRAINT p_multi_check CHECK(price > discounted_price);
|
||||||
|
-- First and third queries will error out, because of conflicts with p_check and
|
||||||
|
-- p_multi_check, respectively.
|
||||||
|
INSERT INTO products VALUES(1, 'product_1', -1, -2);
|
||||||
|
ERROR: new row for relation "products_1450069" violates check constraint "p_check_1450069"
|
||||||
|
DETAIL: Failing row contains (1, product_1, -1, -2).
|
||||||
|
CONTEXT: while executing command on localhost:57638
|
||||||
|
INSERT INTO products VALUES(1, 'product_1', 5, 3);
|
||||||
|
INSERT INTO products VALUES(1, 'product_1', 2, 3);
|
||||||
|
ERROR: new row for relation "products_1450069" violates check constraint "p_multi_check_1450069"
|
||||||
|
DETAIL: Failing row contains (1, product_1, 2, 3).
|
||||||
|
CONTEXT: while executing command on localhost:57638
|
||||||
|
DROP TABLE products;
|
||||||
|
-- Check "CHECK CONSTRAINT" with reference table
|
||||||
|
CREATE TABLE products_ref (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric,
|
||||||
|
discounted_price numeric
|
||||||
|
);
|
||||||
|
SELECT create_reference_table('products_ref');
|
||||||
|
create_reference_table
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Can add column and table check constraints
|
||||||
|
ALTER TABLE products_ref ADD CONSTRAINT p_check CHECK(price > 0);
|
||||||
|
ALTER TABLE products_ref ADD CONSTRAINT p_multi_check CHECK(price > discounted_price);
|
||||||
|
-- First and third queries will error out, because of conflicts with p_check and
|
||||||
|
-- p_multi_check, respectively.
|
||||||
|
INSERT INTO products_ref VALUES(1, 'product_1', -1, -2);
|
||||||
|
ERROR: new row for relation "products_ref_1450100" violates check constraint "p_check_1450100"
|
||||||
|
DETAIL: Failing row contains (1, product_1, -1, -2).
|
||||||
|
CONTEXT: while executing command on localhost:57637
|
||||||
|
INSERT INTO products_ref VALUES(1, 'product_1', 5, 3);
|
||||||
|
INSERT INTO products_ref VALUES(1, 'product_1', 2, 3);
|
||||||
|
ERROR: new row for relation "products_ref_1450100" violates check constraint "p_multi_check_1450100"
|
||||||
|
DETAIL: Failing row contains (1, product_1, 2, 3).
|
||||||
|
CONTEXT: while executing command on localhost:57637
|
||||||
|
DROP TABLE products_ref;
|
||||||
|
-- Check "CHECK CONSTRAINT" with append table
|
||||||
|
CREATE TABLE products_append (
|
||||||
|
product_no int,
|
||||||
|
name varchar(20),
|
||||||
|
price int,
|
||||||
|
discounted_price int
|
||||||
|
);
|
||||||
|
SELECT create_distributed_table('products_append', 'product_no', 'append');
|
||||||
|
create_distributed_table
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Can add column and table check constraints
|
||||||
|
ALTER TABLE products_append ADD CONSTRAINT p_check CHECK(price > 0);
|
||||||
|
ALTER TABLE products_append ADD CONSTRAINT p_multi_check CHECK(price > discounted_price);
|
||||||
|
-- Error out,since the third row conflicting with the p_multi_check
|
||||||
|
\COPY products_append FROM STDIN DELIMITER AS ',';
|
||||||
|
ERROR: new row for relation "products_append_1450101" violates check constraint "p_multi_check"
|
||||||
|
DETAIL: Failing row contains (1, Product_3, 8, 10).
|
||||||
|
DROP TABLE products_append;
|
||||||
|
-- Check "EXCLUSION CONSTRAINT"
|
||||||
|
CREATE TABLE products (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
SELECT create_distributed_table('products', 'product_no');
|
||||||
|
create_distributed_table
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Can only add exclusion constraint on distribution column (or group of columns
|
||||||
|
-- including distribution column)
|
||||||
|
-- Command below should error out since 'name' is not a distribution column
|
||||||
|
ALTER TABLE products ADD CONSTRAINT exc_name EXCLUDE USING btree (name with =);
|
||||||
|
ERROR: cannot create constraint on "products"
|
||||||
|
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
||||||
|
-- We can add composite exclusion
|
||||||
|
ALTER TABLE products ADD CONSTRAINT exc_pno_name EXCLUDE USING btree (product_no with =, name with =);
|
||||||
|
-- 4th command will error out since it conflicts with exc_pno_name constraint
|
||||||
|
INSERT INTO products VALUES(1,'product_1', 5);
|
||||||
|
INSERT INTO products VALUES(1,'product_2', 10);
|
||||||
|
INSERT INTO products VALUES(2,'product_2', 5);
|
||||||
|
INSERT INTO products VALUES(2,'product_2', 5);
|
||||||
|
ERROR: conflicting key value violates exclusion constraint "exc_pno_name_1450126"
|
||||||
|
DETAIL: Key (product_no, name)=(2, product_2) conflicts with existing key (product_no, name)=(2, product_2).
|
||||||
|
CONTEXT: while executing command on localhost:57637
|
||||||
|
DROP TABLE products;
|
||||||
|
-- Check "EXCLUSION CONSTRAINT" with reference table
|
||||||
|
CREATE TABLE products_ref (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
SELECT create_reference_table('products_ref');
|
||||||
|
create_reference_table
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- We can add exclusion constraint on any column
|
||||||
|
ALTER TABLE products_ref ADD CONSTRAINT exc_name EXCLUDE USING btree (name with =);
|
||||||
|
-- We can add composite exclusion because none of pair of rows are conflicting
|
||||||
|
ALTER TABLE products_ref ADD CONSTRAINT exc_pno_name EXCLUDE USING btree (product_no with =, name with =);
|
||||||
|
-- Third insertion will error out, since it has the same name with second insertion
|
||||||
|
INSERT INTO products_ref VALUES(1,'product_1', 5);
|
||||||
|
INSERT INTO products_ref VALUES(1,'product_2', 10);
|
||||||
|
INSERT INTO products_ref VALUES(2,'product_2', 5);
|
||||||
|
ERROR: conflicting key value violates exclusion constraint "exc_name_1450134"
|
||||||
|
DETAIL: Key (name)=(product_2) conflicts with existing key (name)=(product_2).
|
||||||
|
CONTEXT: while executing command on localhost:57637
|
||||||
|
DROP TABLE products_ref;
|
||||||
|
-- Check "EXCLUSION CONSTRAINT" with append table
|
||||||
|
CREATE TABLE products_append (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
SELECT create_distributed_table('products_append', 'product_no','append');
|
||||||
|
create_distributed_table
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Can only add exclusion constraint on distribution column (or group of column
|
||||||
|
-- including distribution column)
|
||||||
|
-- Command below should error out since 'name' is not a distribution column
|
||||||
|
ALTER TABLE products_append ADD CONSTRAINT exc_name EXCLUDE USING btree (name with =);
|
||||||
|
WARNING: table "products_append" has a UNIQUE or EXCLUDE constraint
|
||||||
|
DETAIL: UNIQUE constraints, EXCLUDE constraints, and PRIMARY KEYs on append-partitioned tables cannot be enforced.
|
||||||
|
HINT: Consider using hash partitioning.
|
||||||
|
ERROR: cannot create constraint on "products_append"
|
||||||
|
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
||||||
|
ALTER TABLE products_append ADD CONSTRAINT exc_pno_name EXCLUDE USING btree (product_no with =, name with =);
|
||||||
|
WARNING: table "products_append" has a UNIQUE or EXCLUDE constraint
|
||||||
|
DETAIL: UNIQUE constraints, EXCLUDE constraints, and PRIMARY KEYs on append-partitioned tables cannot be enforced.
|
||||||
|
HINT: Consider using hash partitioning.
|
||||||
|
-- Error out since first and third can not pass the exclusion check.
|
||||||
|
\COPY products_append FROM STDIN DELIMITER AS ',';
|
||||||
|
ERROR: conflicting key value violates exclusion constraint "exc_pno_name_1450135"
|
||||||
|
DETAIL: Key (product_no, name)=(1, Product_1) conflicts with existing key (product_no, name)=(1, Product_1).
|
||||||
|
DROP TABLE products_append;
|
||||||
|
-- Check "NOT NULL"
|
||||||
|
CREATE TABLE products (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
SELECT create_distributed_table('products', 'product_no');
|
||||||
|
create_distributed_table
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER TABLE products ALTER COLUMN name SET NOT NULL;
|
||||||
|
-- Insertions will error out since both product_no and name can not have NULL value
|
||||||
|
INSERT INTO products VALUES(1,NULL,5);
|
||||||
|
ERROR: null value in column "name" violates not-null constraint
|
||||||
|
DETAIL: Failing row contains (1, null, 5).
|
||||||
|
CONTEXT: while executing command on localhost:57638
|
||||||
|
INSERT INTO products VALUES(NULL,'product_1', 5);
|
||||||
|
ERROR: cannot plan INSERT using row with NULL value in partition column
|
||||||
|
DROP TABLE products;
|
||||||
|
-- Check "NOT NULL" with reference table
|
||||||
|
CREATE TABLE products_ref (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
SELECT create_reference_table('products_ref');
|
||||||
|
create_reference_table
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER TABLE products_ref ALTER COLUMN name SET NOT NULL;
|
||||||
|
-- Insertions will error out since both product_no and name can not have NULL value
|
||||||
|
INSERT INTO products_ref VALUES(1,NULL,5);
|
||||||
|
ERROR: null value in column "name" violates not-null constraint
|
||||||
|
DETAIL: Failing row contains (1, null, 5).
|
||||||
|
CONTEXT: while executing command on localhost:57637
|
||||||
|
INSERT INTO products_ref VALUES(NULL,'product_1', 5);
|
||||||
|
DROP TABLE products_ref;
|
||||||
|
-- Check "NOT NULL" with append table
|
||||||
|
CREATE TABLE products_append (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
SELECT create_distributed_table('products_append', 'product_no', 'append');
|
||||||
|
create_distributed_table
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER TABLE products_append ALTER COLUMN name SET NOT NULL;
|
||||||
|
-- Error out since name and product_no columns can not handle NULL value.
|
||||||
|
\COPY products_append FROM STDIN DELIMITER AS ',';
|
||||||
|
DROP TABLE products_append;
|
||||||
|
-- Tests for ADD CONSTRAINT is not only subcommand
|
||||||
|
CREATE TABLE products (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
SELECT create_distributed_table('products', 'product_no');
|
||||||
|
create_distributed_table
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Should error out since add constraint is not the single subcommand
|
||||||
|
ALTER TABLE products ADD CONSTRAINT unn_1 UNIQUE(product_no, price), ADD CONSTRAINT unn_2 UNIQUE(product_no, name);
|
||||||
|
ERROR: cannot execute ADD CONSTRAINT command with other subcommands
|
||||||
|
HINT: You can issue each subcommand separately
|
||||||
|
-- Tests for constraints without name
|
||||||
|
-- Commands below should error out since constraints do not have the name
|
||||||
|
ALTER TABLE products ADD UNIQUE(product_no);
|
||||||
|
ERROR: cannot create constraint without a name on a distributed table
|
||||||
|
ALTER TABLE products ADD PRIMARY KEY(product_no);
|
||||||
|
ERROR: cannot create constraint without a name on a distributed table
|
||||||
|
ALTER TABLE products ADD CHECK(product_no <> 0);
|
||||||
|
ERROR: cannot create constraint without a name on a distributed table
|
||||||
|
ALTER TABLE products ADD EXCLUDE USING btree (product_no with =);
|
||||||
|
ERROR: cannot create constraint without a name on a distributed table
|
||||||
|
DROP TABLE products;
|
||||||
|
-- Tests with transactions
|
||||||
|
CREATE TABLE products (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric,
|
||||||
|
discounted_price numeric
|
||||||
|
);
|
||||||
|
SELECT create_distributed_table('products', 'product_no');
|
||||||
|
create_distributed_table
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO products VALUES(1,'product_1', 5);
|
||||||
|
-- Should error out since conflicts with the above single-shard data modification command.
|
||||||
|
ALTER TABLE products ADD CONSTRAINT unn_pno UNIQUE(product_no);
|
||||||
|
ERROR: distributed DDL commands must not appear within transaction blocks containing single-shard data modifications
|
||||||
|
ROLLBACK;
|
||||||
|
BEGIN;
|
||||||
|
-- Add constraints
|
||||||
|
ALTER TABLE products ADD CONSTRAINT unn_pno UNIQUE(product_no);
|
||||||
|
ALTER TABLE products ADD CONSTRAINT check_price CHECK(price > discounted_price);
|
||||||
|
ALTER TABLE products ALTER COLUMN product_no SET NOT NULL;
|
||||||
|
ALTER TABLE products ADD CONSTRAINT p_key_product PRIMARY KEY(product_no);
|
||||||
|
-- Single shard DML command can't be located in the same transaction with above commands.
|
||||||
|
INSERT INTO products VALUES(1,'product_1', 10, 8);
|
||||||
|
ERROR: single-shard DML commands must not appear in transaction blocks which contain multi-shard data modifications
|
||||||
|
ROLLBACK;
|
||||||
|
-- There should be no constraint on master and worker(s)
|
||||||
|
\d products
|
||||||
|
Table "public.products"
|
||||||
|
Column | Type | Modifiers
|
||||||
|
------------------+---------+-----------
|
||||||
|
product_no | integer |
|
||||||
|
name | text |
|
||||||
|
price | numeric |
|
||||||
|
discounted_price | numeric |
|
||||||
|
|
||||||
|
\c - - - :worker_1_port
|
||||||
|
\d products_1450199
|
||||||
|
\c - - - :master_port
|
||||||
|
-- Tests to check the effect of rollback
|
||||||
|
BEGIN;
|
||||||
|
-- Add constraints (which will be rollbacked)
|
||||||
|
ALTER TABLE products ADD CONSTRAINT unn_pno UNIQUE(product_no);
|
||||||
|
NOTICE: using one-phase commit for distributed DDL commands
|
||||||
|
HINT: You can enable two-phase commit for extra safety with: SET citus.multi_shard_commit_protocol TO '2pc'
|
||||||
|
ALTER TABLE products ADD CONSTRAINT check_price CHECK(price > discounted_price);
|
||||||
|
ALTER TABLE products ADD CONSTRAINT p_key_product PRIMARY KEY(product_no);
|
||||||
|
ROLLBACK;
|
||||||
|
-- There should be no constraint on master and worker(s)
|
||||||
|
\d products
|
||||||
|
Table "public.products"
|
||||||
|
Column | Type | Modifiers
|
||||||
|
------------------+---------+-----------
|
||||||
|
product_no | integer |
|
||||||
|
name | text |
|
||||||
|
price | numeric |
|
||||||
|
discounted_price | numeric |
|
||||||
|
|
||||||
|
\c - - - :worker_1_port
|
||||||
|
\d products_1450199
|
||||||
|
\c - - - :master_port
|
||||||
|
DROP TABLE products;
|
|
@ -438,21 +438,21 @@ FROM
|
||||||
WHERE
|
WHERE
|
||||||
li.l_quantity > 25
|
li.l_quantity > 25
|
||||||
ORDER BY
|
ORDER BY
|
||||||
li.l_quantity
|
li.l_quantity, li.l_partkey, o.o_custkey
|
||||||
LIMIT 10 OFFSET 20;
|
LIMIT 10 OFFSET 20;
|
||||||
DEBUG: push down of limit count: 30
|
DEBUG: push down of limit count: 30
|
||||||
l_partkey | o_custkey | l_quantity
|
l_partkey | o_custkey | l_quantity
|
||||||
-----------+-----------+------------
|
-----------+-----------+------------
|
||||||
154380 | 421 | 26.00
|
25221 | 656 | 26.00
|
||||||
103981 | 325 | 26.00
|
25373 | 1369 | 26.00
|
||||||
77886 | 817 | 26.00
|
27331 | 571 | 26.00
|
||||||
147369 | 755 | 26.00
|
27699 | 1150 | 26.00
|
||||||
78175 | 1075 | 26.00
|
28226 | 913 | 26.00
|
||||||
109784 | 1268 | 26.00
|
|
||||||
28635 | 1207 | 26.00
|
28635 | 1207 | 26.00
|
||||||
188845 | 554 | 26.00
|
29101 | 1283 | 26.00
|
||||||
189398 | 844 | 26.00
|
31143 | 640 | 26.00
|
||||||
71383 | 1397 | 26.00
|
31239 | 685 | 26.00
|
||||||
|
33646 | 860 | 26.00
|
||||||
(10 rows)
|
(10 rows)
|
||||||
|
|
||||||
RESET client_min_messages;
|
RESET client_min_messages;
|
||||||
|
|
|
@ -40,7 +40,7 @@ CREATE TABLE pk_on_non_part_col
|
||||||
other_col integer PRIMARY KEY
|
other_col integer PRIMARY KEY
|
||||||
);
|
);
|
||||||
SELECT master_create_distributed_table('pk_on_non_part_col', 'partition_col', 'hash');
|
SELECT master_create_distributed_table('pk_on_non_part_col', 'partition_col', 'hash');
|
||||||
ERROR: cannot distribute relation: "pk_on_non_part_col"
|
ERROR: cannot create constraint on "pk_on_non_part_col"
|
||||||
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
||||||
CREATE TABLE uq_on_non_part_col
|
CREATE TABLE uq_on_non_part_col
|
||||||
(
|
(
|
||||||
|
@ -48,7 +48,7 @@ CREATE TABLE uq_on_non_part_col
|
||||||
other_col integer UNIQUE
|
other_col integer UNIQUE
|
||||||
);
|
);
|
||||||
SELECT master_create_distributed_table('uq_on_non_part_col', 'partition_col', 'hash');
|
SELECT master_create_distributed_table('uq_on_non_part_col', 'partition_col', 'hash');
|
||||||
ERROR: cannot distribute relation: "uq_on_non_part_col"
|
ERROR: cannot create constraint on "uq_on_non_part_col"
|
||||||
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
||||||
CREATE TABLE ex_on_non_part_col
|
CREATE TABLE ex_on_non_part_col
|
||||||
(
|
(
|
||||||
|
@ -57,7 +57,7 @@ CREATE TABLE ex_on_non_part_col
|
||||||
EXCLUDE (other_col WITH =)
|
EXCLUDE (other_col WITH =)
|
||||||
);
|
);
|
||||||
SELECT master_create_distributed_table('ex_on_non_part_col', 'partition_col', 'hash');
|
SELECT master_create_distributed_table('ex_on_non_part_col', 'partition_col', 'hash');
|
||||||
ERROR: cannot distribute relation: "ex_on_non_part_col"
|
ERROR: cannot create constraint on "ex_on_non_part_col"
|
||||||
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
||||||
-- now show that Citus can distribute unique and EXCLUDE constraints that
|
-- now show that Citus can distribute unique and EXCLUDE constraints that
|
||||||
-- include the partition column for hash-partitioned tables.
|
-- include the partition column for hash-partitioned tables.
|
||||||
|
@ -187,7 +187,7 @@ CREATE TABLE ex_wrong_operator
|
||||||
EXCLUDE USING gist (other_col WITH =, partition_col WITH &&)
|
EXCLUDE USING gist (other_col WITH =, partition_col WITH &&)
|
||||||
);
|
);
|
||||||
SELECT master_create_distributed_table('ex_wrong_operator', 'partition_col', 'hash');
|
SELECT master_create_distributed_table('ex_wrong_operator', 'partition_col', 'hash');
|
||||||
ERROR: cannot distribute relation: "ex_wrong_operator"
|
ERROR: cannot create constraint on "ex_wrong_operator"
|
||||||
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
||||||
CREATE TABLE ex_overlaps
|
CREATE TABLE ex_overlaps
|
||||||
(
|
(
|
||||||
|
@ -344,7 +344,7 @@ CREATE TABLE ex_wrong_operator_named
|
||||||
CONSTRAINT ex_wrong_operator_named_exclude EXCLUDE USING gist (other_col WITH =, partition_col WITH &&)
|
CONSTRAINT ex_wrong_operator_named_exclude EXCLUDE USING gist (other_col WITH =, partition_col WITH &&)
|
||||||
);
|
);
|
||||||
SELECT master_create_distributed_table('ex_wrong_operator_named', 'partition_col', 'hash');
|
SELECT master_create_distributed_table('ex_wrong_operator_named', 'partition_col', 'hash');
|
||||||
ERROR: cannot distribute relation: "ex_wrong_operator_named"
|
ERROR: cannot create constraint on "ex_wrong_operator_named"
|
||||||
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
||||||
CREATE TABLE ex_overlaps_named
|
CREATE TABLE ex_overlaps_named
|
||||||
(
|
(
|
||||||
|
|
|
@ -433,13 +433,11 @@ DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operatio
|
||||||
-- test foreign constraint creation with multiple subcommands
|
-- test foreign constraint creation with multiple subcommands
|
||||||
ALTER TABLE referencing_table ADD CONSTRAINT test_constraint FOREIGN KEY(ref_id) REFERENCES referenced_table(id),
|
ALTER TABLE referencing_table ADD CONSTRAINT test_constraint FOREIGN KEY(ref_id) REFERENCES referenced_table(id),
|
||||||
ADD CONSTRAINT test_constraint FOREIGN KEY(id) REFERENCES referenced_table(test_column);
|
ADD CONSTRAINT test_constraint FOREIGN KEY(id) REFERENCES referenced_table(test_column);
|
||||||
ERROR: cannot create foreign key constraint
|
ERROR: cannot execute ADD CONSTRAINT command with other subcommands
|
||||||
DETAIL: Citus cannot execute ADD CONSTRAINT FOREIGN KEY command together with other subcommands.
|
|
||||||
HINT: You can issue each subcommand separately
|
HINT: You can issue each subcommand separately
|
||||||
-- test foreign constraint creation without giving explicit name
|
-- test foreign constraint creation without giving explicit name
|
||||||
ALTER TABLE referencing_table ADD FOREIGN KEY(ref_id) REFERENCES referenced_table(id);
|
ALTER TABLE referencing_table ADD FOREIGN KEY(ref_id) REFERENCES referenced_table(id);
|
||||||
ERROR: cannot create foreign key constraint
|
ERROR: cannot create constraint without a name on a distributed table
|
||||||
DETAIL: Creating foreign constraint without a name on a distributed table is currently not supported.
|
|
||||||
-- test foreign constraint creation on NOT co-located tables
|
-- test foreign constraint creation on NOT co-located tables
|
||||||
ALTER TABLE referencing_table ADD CONSTRAINT test_constraint FOREIGN KEY(ref_id) REFERENCES referenced_table(id);
|
ALTER TABLE referencing_table ADD CONSTRAINT test_constraint FOREIGN KEY(ref_id) REFERENCES referenced_table(id);
|
||||||
ERROR: cannot create foreign key constraint
|
ERROR: cannot create foreign key constraint
|
||||||
|
@ -463,8 +461,7 @@ SELECT create_distributed_table('referencing_table', 'ref_id', 'hash');
|
||||||
|
|
||||||
-- columns for the referenced table is empty
|
-- columns for the referenced table is empty
|
||||||
ALTER TABLE referencing_table ADD CONSTRAINT test_constraint FOREIGN KEY(ref_id) REFERENCES referenced_table ON DELETE CASCADE;
|
ALTER TABLE referencing_table ADD CONSTRAINT test_constraint FOREIGN KEY(ref_id) REFERENCES referenced_table ON DELETE CASCADE;
|
||||||
ERROR: cannot create foreign key constraint because referenced column list is empty
|
ERROR: number of referencing and referenced columns for foreign key disagree
|
||||||
HINT: Add column names to "REFERENCES" part of the statement.
|
|
||||||
-- test foreign constraint creation on non-partition columns
|
-- test foreign constraint creation on non-partition columns
|
||||||
ALTER TABLE referencing_table ADD CONSTRAINT test_constraint FOREIGN KEY(id) REFERENCES referenced_table(id);
|
ALTER TABLE referencing_table ADD CONSTRAINT test_constraint FOREIGN KEY(id) REFERENCES referenced_table(id);
|
||||||
ERROR: cannot create foreign key constraint
|
ERROR: cannot create foreign key constraint
|
||||||
|
@ -475,8 +472,7 @@ ERROR: cannot create foreign key constraint
|
||||||
DETAIL: 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.
|
DETAIL: 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.
|
||||||
-- test foreign constraint creation while column list are not in same length
|
-- test foreign constraint creation while column list are not in same length
|
||||||
ALTER TABLE referencing_table ADD CONSTRAINT test_constraint FOREIGN KEY(ref_id) REFERENCES referenced_table(id, test_column);
|
ALTER TABLE referencing_table ADD CONSTRAINT test_constraint FOREIGN KEY(ref_id) REFERENCES referenced_table(id, test_column);
|
||||||
ERROR: cannot create foreign key constraint
|
ERROR: number of referencing and referenced columns for foreign key disagree
|
||||||
DETAIL: Referencing column list and referenced column list must be in same size.
|
|
||||||
-- test foreign constraint creation while existing tables does not satisfy the constraint
|
-- test foreign constraint creation while existing tables does not satisfy the constraint
|
||||||
INSERT INTO referencing_table VALUES(1, 1);
|
INSERT INTO referencing_table VALUES(1, 1);
|
||||||
ALTER TABLE referencing_table ADD CONSTRAINT test_constraint FOREIGN KEY(ref_id) REFERENCES referenced_table(id);
|
ALTER TABLE referencing_table ADD CONSTRAINT test_constraint FOREIGN KEY(ref_id) REFERENCES referenced_table(id);
|
||||||
|
@ -794,8 +790,7 @@ SELECT create_distributed_table('referenced_by_reference_table', 'id');
|
||||||
|
|
||||||
CREATE TABLE reference_table(id int, referencing_column int REFERENCES referenced_by_reference_table(id));
|
CREATE TABLE reference_table(id int, referencing_column int REFERENCES referenced_by_reference_table(id));
|
||||||
SELECT create_reference_table('reference_table');
|
SELECT create_reference_table('reference_table');
|
||||||
ERROR: cannot create foreign key constraint
|
ERROR: cannot create foreign key constraint from or to reference tables
|
||||||
DETAIL: Foreign key constraints are not allowed from or to reference tables.
|
|
||||||
-- test foreign key creation on CREATE TABLE to reference table
|
-- test foreign key creation on CREATE TABLE to reference table
|
||||||
DROP TABLE reference_table;
|
DROP TABLE reference_table;
|
||||||
CREATE TABLE reference_table(id int PRIMARY KEY, referencing_column int);
|
CREATE TABLE reference_table(id int PRIMARY KEY, referencing_column int);
|
||||||
|
@ -807,13 +802,11 @@ SELECT create_reference_table('reference_table');
|
||||||
|
|
||||||
CREATE TABLE references_to_reference_table(id int, referencing_column int REFERENCES reference_table(id));
|
CREATE TABLE references_to_reference_table(id int, referencing_column int REFERENCES reference_table(id));
|
||||||
SELECT create_distributed_table('references_to_reference_table', 'referencing_column');
|
SELECT create_distributed_table('references_to_reference_table', 'referencing_column');
|
||||||
ERROR: cannot create foreign key constraint
|
ERROR: cannot create foreign key constraint from or to reference tables
|
||||||
DETAIL: Foreign key constraints are not allowed from or to reference tables.
|
|
||||||
-- test foreign key creation on CREATE TABLE from + to reference table
|
-- test foreign key creation on CREATE TABLE from + to reference table
|
||||||
CREATE TABLE reference_table_second(id int, referencing_column int REFERENCES reference_table(id));
|
CREATE TABLE reference_table_second(id int, referencing_column int REFERENCES reference_table(id));
|
||||||
SELECT create_reference_table('reference_table_second');
|
SELECT create_reference_table('reference_table_second');
|
||||||
ERROR: cannot create foreign key constraint
|
ERROR: cannot create foreign key constraint from or to reference tables
|
||||||
DETAIL: Foreign key constraints are not allowed from or to reference tables.
|
|
||||||
-- test foreign key creation on CREATE TABLE from reference table to local table
|
-- test foreign key creation on CREATE TABLE from reference table to local table
|
||||||
CREATE TABLE referenced_local_table(id int PRIMARY KEY, other_column int);
|
CREATE TABLE referenced_local_table(id int PRIMARY KEY, other_column int);
|
||||||
DROP TABLE reference_table CASCADE;
|
DROP TABLE reference_table CASCADE;
|
||||||
|
@ -822,8 +815,7 @@ DETAIL: drop cascades to constraint references_to_reference_table_referencing_c
|
||||||
drop cascades to constraint reference_table_second_referencing_column_fkey on table reference_table_second
|
drop cascades to constraint reference_table_second_referencing_column_fkey on table reference_table_second
|
||||||
CREATE TABLE reference_table(id int, referencing_column int REFERENCES referenced_local_table(id));
|
CREATE TABLE reference_table(id int, referencing_column int REFERENCES referenced_local_table(id));
|
||||||
SELECT create_reference_table('reference_table');
|
SELECT create_reference_table('reference_table');
|
||||||
ERROR: cannot create foreign key constraint
|
ERROR: cannot create foreign key constraint from or to reference tables
|
||||||
DETAIL: Foreign key constraints are not allowed from or to reference tables.
|
|
||||||
-- test foreign key creation on CREATE TABLE on self referencing reference table
|
-- test foreign key creation on CREATE TABLE on self referencing reference table
|
||||||
CREATE TABLE self_referencing_reference_table(
|
CREATE TABLE self_referencing_reference_table(
|
||||||
id int,
|
id int,
|
||||||
|
@ -833,8 +825,7 @@ CREATE TABLE self_referencing_reference_table(
|
||||||
FOREIGN KEY(id, other_column_ref) REFERENCES self_referencing_reference_table(id, other_column)
|
FOREIGN KEY(id, other_column_ref) REFERENCES self_referencing_reference_table(id, other_column)
|
||||||
);
|
);
|
||||||
SELECT create_reference_table('self_referencing_reference_table');
|
SELECT create_reference_table('self_referencing_reference_table');
|
||||||
ERROR: cannot create foreign key constraint
|
ERROR: cannot create foreign key constraint from or to reference tables
|
||||||
DETAIL: Foreign key constraints are not allowed from or to reference tables.
|
|
||||||
-- test foreign key creation on ALTER TABLE from reference table
|
-- test foreign key creation on ALTER TABLE from reference table
|
||||||
DROP TABLE reference_table;
|
DROP TABLE reference_table;
|
||||||
CREATE TABLE reference_table(id int PRIMARY KEY, referencing_column int);
|
CREATE TABLE reference_table(id int PRIMARY KEY, referencing_column int);
|
||||||
|
@ -845,8 +836,7 @@ SELECT create_reference_table('reference_table');
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
ALTER TABLE reference_table ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES referenced_by_reference_table(id);
|
ALTER TABLE reference_table ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES referenced_by_reference_table(id);
|
||||||
ERROR: cannot create foreign key constraint
|
ERROR: cannot create foreign key constraint from or to reference tables
|
||||||
DETAIL: Foreign key constraints are not allowed from or to reference tables.
|
|
||||||
-- test foreign key creation on ALTER TABLE to reference table
|
-- test foreign key creation on ALTER TABLE to reference table
|
||||||
DROP TABLE references_to_reference_table;
|
DROP TABLE references_to_reference_table;
|
||||||
CREATE TABLE references_to_reference_table(id int, referencing_column int);
|
CREATE TABLE references_to_reference_table(id int, referencing_column int);
|
||||||
|
@ -857,8 +847,7 @@ SELECT create_distributed_table('references_to_reference_table', 'referencing_co
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
ALTER TABLE references_to_reference_table ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES reference_table(id);
|
ALTER TABLE references_to_reference_table ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES reference_table(id);
|
||||||
ERROR: cannot create foreign key constraint
|
ERROR: cannot create foreign key constraint from or to reference tables
|
||||||
DETAIL: Foreign key constraints are not allowed from or to reference tables.
|
|
||||||
-- test foreign key creation on ALTER TABLE from + to reference table
|
-- test foreign key creation on ALTER TABLE from + to reference table
|
||||||
DROP TABLE reference_table_second;
|
DROP TABLE reference_table_second;
|
||||||
CREATE TABLE reference_table_second(id int, referencing_column int);
|
CREATE TABLE reference_table_second(id int, referencing_column int);
|
||||||
|
@ -869,8 +858,7 @@ SELECT create_reference_table('reference_table_second');
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
ALTER TABLE reference_table_second ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES reference_table(id);
|
ALTER TABLE reference_table_second ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES reference_table(id);
|
||||||
ERROR: cannot create foreign key constraint
|
ERROR: cannot create foreign key constraint from or to reference tables
|
||||||
DETAIL: Foreign key constraints are not allowed from or to reference tables.
|
|
||||||
-- test foreign key creation on ALTER TABLE from reference table to local table
|
-- test foreign key creation on ALTER TABLE from reference table to local table
|
||||||
DROP TABLE reference_table;
|
DROP TABLE reference_table;
|
||||||
CREATE TABLE reference_table(id int PRIMARY KEY, referencing_column int);
|
CREATE TABLE reference_table(id int PRIMARY KEY, referencing_column int);
|
||||||
|
@ -881,8 +869,7 @@ SELECT create_reference_table('reference_table');
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
ALTER TABLE reference_table ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES referenced_local_table(id);
|
ALTER TABLE reference_table ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES referenced_local_table(id);
|
||||||
ERROR: cannot create foreign key constraint
|
ERROR: relation referenced_local_table is not distributed
|
||||||
DETAIL: Foreign key constraints are not allowed from or to reference tables.
|
|
||||||
-- test foreign key creation on ALTER TABLE on self referencing reference table
|
-- test foreign key creation on ALTER TABLE on self referencing reference table
|
||||||
DROP TABLE self_referencing_reference_table;
|
DROP TABLE self_referencing_reference_table;
|
||||||
CREATE TABLE self_referencing_reference_table(
|
CREATE TABLE self_referencing_reference_table(
|
||||||
|
@ -898,7 +885,6 @@ SELECT create_reference_table('self_referencing_reference_table');
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
ALTER TABLE self_referencing_reference_table ADD CONSTRAINT fk FOREIGN KEY(id, other_column_ref) REFERENCES self_referencing_reference_table(id, other_column);
|
ALTER TABLE self_referencing_reference_table ADD CONSTRAINT fk FOREIGN KEY(id, other_column_ref) REFERENCES self_referencing_reference_table(id, other_column);
|
||||||
ERROR: cannot create foreign key constraint
|
ERROR: cannot create foreign key constraint from or to reference tables
|
||||||
DETAIL: Foreign key constraints are not allowed from or to reference tables.
|
|
||||||
-- we no longer need those tables
|
-- we no longer need those tables
|
||||||
DROP TABLE referenced_by_reference_table, references_to_reference_table, reference_table, reference_table_second, referenced_local_table, self_referencing_reference_table;
|
DROP TABLE referenced_by_reference_table, references_to_reference_table, reference_table, reference_table_second, referenced_local_table, self_referencing_reference_table;
|
||||||
|
|
|
@ -1226,11 +1226,13 @@ FROM
|
||||||
NATURAL JOIN pg_dist_shard
|
NATURAL JOIN pg_dist_shard
|
||||||
NATURAL JOIN pg_dist_shard_placement
|
NATURAL JOIN pg_dist_shard_placement
|
||||||
WHERE
|
WHERE
|
||||||
logicalrelid = 'mx_ref'::regclass;
|
logicalrelid = 'mx_ref'::regclass
|
||||||
|
ORDER BY
|
||||||
|
nodeport;
|
||||||
logicalrelid | partmethod | repmodel | shardid | placementid | nodename | nodeport
|
logicalrelid | partmethod | repmodel | shardid | placementid | nodename | nodeport
|
||||||
--------------+------------+----------+---------+-------------+-----------+----------
|
--------------+------------+----------+---------+-------------+-----------+----------
|
||||||
mx_ref | n | t | 1310183 | 100184 | localhost | 57638
|
|
||||||
mx_ref | n | t | 1310183 | 100183 | localhost | 57637
|
mx_ref | n | t | 1310183 | 100183 | localhost | 57637
|
||||||
|
mx_ref | n | t | 1310183 | 100184 | localhost | 57638
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -77,14 +77,11 @@ ALTER TABLE name_lengths ADD COLUMN date_col_12345678901234567890123456789012345
|
||||||
ALTER TABLE name_lengths ADD COLUMN int_col_12345678901234567890123456789012345678901234567890 INTEGER DEFAULT 1;
|
ALTER TABLE name_lengths ADD COLUMN int_col_12345678901234567890123456789012345678901234567890 INTEGER DEFAULT 1;
|
||||||
-- Placeholders for unsupported ALTER TABLE to add constraints with implicit names that are likely too long
|
-- Placeholders for unsupported ALTER TABLE to add constraints with implicit names that are likely too long
|
||||||
ALTER TABLE name_lengths ADD UNIQUE (float_col_12345678901234567890123456789012345678901234567890);
|
ALTER TABLE name_lengths ADD UNIQUE (float_col_12345678901234567890123456789012345678901234567890);
|
||||||
ERROR: cannot create constraint
|
ERROR: cannot create constraint without a name on a distributed table
|
||||||
DETAIL: Citus cannot execute ADD CONSTRAINT command other than ADD CONSTRAINT FOREIGN KEY.
|
|
||||||
ALTER TABLE name_lengths ADD EXCLUDE (int_col_12345678901234567890123456789012345678901234567890 WITH =);
|
ALTER TABLE name_lengths ADD EXCLUDE (int_col_12345678901234567890123456789012345678901234567890 WITH =);
|
||||||
ERROR: cannot create constraint
|
ERROR: cannot create constraint without a name on a distributed table
|
||||||
DETAIL: Citus cannot execute ADD CONSTRAINT command other than ADD CONSTRAINT FOREIGN KEY.
|
|
||||||
ALTER TABLE name_lengths ADD CHECK (date_col_12345678901234567890123456789012345678901234567890 > '2014-01-01'::date);
|
ALTER TABLE name_lengths ADD CHECK (date_col_12345678901234567890123456789012345678901234567890 > '2014-01-01'::date);
|
||||||
ERROR: cannot create constraint
|
ERROR: cannot create constraint without a name on a distributed table
|
||||||
DETAIL: Citus cannot execute ADD CONSTRAINT command other than ADD CONSTRAINT FOREIGN KEY.
|
|
||||||
\c - - - :worker_1_port
|
\c - - - :worker_1_port
|
||||||
\d name_lengths_*
|
\d name_lengths_*
|
||||||
Table "public.name_lengths_225002"
|
Table "public.name_lengths_225002"
|
||||||
|
@ -112,14 +109,14 @@ Indexes:
|
||||||
\c - - - :master_port
|
\c - - - :master_port
|
||||||
-- Placeholders for unsupported add constraints with EXPLICIT names that are too long
|
-- Placeholders for unsupported add constraints with EXPLICIT names that are too long
|
||||||
ALTER TABLE name_lengths ADD CONSTRAINT nl_unique_12345678901234567890123456789012345678901234567890 UNIQUE (float_col_12345678901234567890123456789012345678901234567890);
|
ALTER TABLE name_lengths ADD CONSTRAINT nl_unique_12345678901234567890123456789012345678901234567890 UNIQUE (float_col_12345678901234567890123456789012345678901234567890);
|
||||||
ERROR: cannot create constraint
|
ERROR: cannot create constraint on "name_lengths"
|
||||||
DETAIL: Citus cannot execute ADD CONSTRAINT command other than ADD CONSTRAINT FOREIGN KEY.
|
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
||||||
ALTER TABLE name_lengths ADD CONSTRAINT nl_exclude_12345678901234567890123456789012345678901234567890 EXCLUDE (int_col_12345678901234567890123456789012345678901234567890 WITH =);
|
ALTER TABLE name_lengths ADD CONSTRAINT nl_exclude_12345678901234567890123456789012345678901234567890 EXCLUDE (int_col_12345678901234567890123456789012345678901234567890 WITH =);
|
||||||
ERROR: cannot create constraint
|
ERROR: cannot create constraint on "name_lengths"
|
||||||
DETAIL: Citus cannot execute ADD CONSTRAINT command other than ADD CONSTRAINT FOREIGN KEY.
|
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or PRIMARY KEY constraints that do not include the partition column (with an equality operator if EXCLUDE).
|
||||||
ALTER TABLE name_lengths ADD CONSTRAINT nl_checky_12345678901234567890123456789012345678901234567890 CHECK (date_col_12345678901234567890123456789012345678901234567890 >= '2014-01-01'::date);
|
ALTER TABLE name_lengths ADD CONSTRAINT nl_checky_12345678901234567890123456789012345678901234567890 CHECK (date_col_12345678901234567890123456789012345678901234567890 >= '2014-01-01'::date);
|
||||||
ERROR: cannot create constraint
|
NOTICE: using one-phase commit for distributed DDL commands
|
||||||
DETAIL: Citus cannot execute ADD CONSTRAINT command other than ADD CONSTRAINT FOREIGN KEY.
|
HINT: You can enable two-phase commit for extra safety with: SET citus.multi_shard_commit_protocol TO '2pc'
|
||||||
\c - - - :worker_1_port
|
\c - - - :worker_1_port
|
||||||
\d nl_*
|
\d nl_*
|
||||||
\c - - - :master_port
|
\c - - - :master_port
|
||||||
|
|
|
@ -1362,7 +1362,7 @@ ALTER TABLE reference_table_ddl RENAME TO reference_table_ddl_test;
|
||||||
ERROR: renaming distributed tables is currently unsupported
|
ERROR: renaming distributed tables is currently unsupported
|
||||||
ALTER TABLE reference_table_ddl SET WITH OIDS;
|
ALTER TABLE reference_table_ddl SET WITH OIDS;
|
||||||
ERROR: alter table command is currently unsupported
|
ERROR: alter table command is currently unsupported
|
||||||
DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP CONSTRAINT FOREIGN KEY and TYPE subcommands are supported.
|
DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP CONSTRAINT and TYPE subcommands are supported.
|
||||||
-- now test reference tables against some helper UDFs that Citus provides
|
-- now test reference tables against some helper UDFs that Citus provides
|
||||||
-- cannot delete / drop shards from a reference table
|
-- cannot delete / drop shards from a reference table
|
||||||
SELECT master_apply_delete_command('DELETE FROM reference_table_ddl');
|
SELECT master_apply_delete_command('DELETE FROM reference_table_ddl');
|
||||||
|
|
|
@ -96,6 +96,7 @@ test: multi_shard_modify
|
||||||
# ----------
|
# ----------
|
||||||
test: multi_index_statements
|
test: multi_index_statements
|
||||||
test: multi_alter_table_statements
|
test: multi_alter_table_statements
|
||||||
|
test: multi_alter_table_add_constraints
|
||||||
|
|
||||||
# ----------
|
# ----------
|
||||||
# multi_create_schema tests creation, loading, and querying of a table in a new
|
# multi_create_schema tests creation, loading, and querying of a table in a new
|
||||||
|
|
|
@ -332,7 +332,7 @@ ALTER TABLE lineitem_alter ADD COLUMN int_column1 INTEGER,
|
||||||
ALTER TABLE lineitem_alter ADD COLUMN int_column3 INTEGER,
|
ALTER TABLE lineitem_alter ADD COLUMN int_column3 INTEGER,
|
||||||
ALTER COLUMN int_column1 SET STATISTICS 10;
|
ALTER COLUMN int_column1 SET STATISTICS 10;
|
||||||
ERROR: alter table command is currently unsupported
|
ERROR: alter table command is currently unsupported
|
||||||
DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP CONSTRAINT FOREIGN KEY and TYPE subcommands are supported.
|
DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP CONSTRAINT and TYPE subcommands are supported.
|
||||||
ALTER TABLE lineitem_alter DROP COLUMN int_column1, DROP COLUMN int_column2;
|
ALTER TABLE lineitem_alter DROP COLUMN int_column1, DROP COLUMN int_column2;
|
||||||
\d lineitem_alter
|
\d lineitem_alter
|
||||||
Table "public.lineitem_alter"
|
Table "public.lineitem_alter"
|
||||||
|
@ -364,12 +364,12 @@ ERROR: cannot execute ALTER TABLE command involving partition column
|
||||||
-- Verify that we error out on unsupported statement types
|
-- Verify that we error out on unsupported statement types
|
||||||
ALTER TABLE lineitem_alter ALTER COLUMN l_orderkey SET STATISTICS 100;
|
ALTER TABLE lineitem_alter ALTER COLUMN l_orderkey SET STATISTICS 100;
|
||||||
ERROR: alter table command is currently unsupported
|
ERROR: alter table command is currently unsupported
|
||||||
DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP CONSTRAINT FOREIGN KEY and TYPE subcommands are supported.
|
DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP CONSTRAINT and TYPE subcommands are supported.
|
||||||
ALTER TABLE lineitem_alter DROP CONSTRAINT IF EXISTS non_existent_contraint;
|
ALTER TABLE lineitem_alter DROP CONSTRAINT IF EXISTS non_existent_contraint;
|
||||||
NOTICE: constraint "non_existent_contraint" of relation "lineitem_alter" does not exist, skipping
|
NOTICE: constraint "non_existent_contraint" of relation "lineitem_alter" does not exist, skipping
|
||||||
ALTER TABLE lineitem_alter SET WITHOUT OIDS;
|
ALTER TABLE lineitem_alter SET WITHOUT OIDS;
|
||||||
ERROR: alter table command is currently unsupported
|
ERROR: alter table command is currently unsupported
|
||||||
DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP CONSTRAINT FOREIGN KEY and TYPE subcommands are supported.
|
DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP CONSTRAINT and TYPE subcommands are supported.
|
||||||
-- Verify that we error out in case of postgres errors on supported statement
|
-- Verify that we error out in case of postgres errors on supported statement
|
||||||
-- types
|
-- types
|
||||||
ALTER TABLE lineitem_alter ADD COLUMN new_column non_existent_type;
|
ALTER TABLE lineitem_alter ADD COLUMN new_column non_existent_type;
|
||||||
|
|
|
@ -0,0 +1,425 @@
|
||||||
|
--
|
||||||
|
-- MULTI_ALTER_TABLE_ADD_CONSTRAINTS
|
||||||
|
--
|
||||||
|
-- Test checks whether constraints of distributed tables can be adjusted using
|
||||||
|
-- the ALTER TABLE ... ADD CONSTRAINT ... command.
|
||||||
|
|
||||||
|
ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 1450000;
|
||||||
|
ALTER SEQUENCE pg_catalog.pg_dist_jobid_seq RESTART 1450000;
|
||||||
|
|
||||||
|
-- Check "PRIMARY KEY CONSTRAINT"
|
||||||
|
CREATE TABLE products (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT create_distributed_table('products', 'product_no');
|
||||||
|
|
||||||
|
-- Can only add primary key constraint on distribution column (or group of columns
|
||||||
|
-- including distribution column)
|
||||||
|
-- Command below should error out since 'name' is not a distribution column
|
||||||
|
ALTER TABLE products ADD CONSTRAINT p_key PRIMARY KEY(name);
|
||||||
|
ALTER TABLE products ADD CONSTRAINT p_key PRIMARY KEY(product_no);
|
||||||
|
|
||||||
|
INSERT INTO products VALUES(1, 'product_1', 1);
|
||||||
|
|
||||||
|
-- Should error out, since we are trying to add a new row having a value on p_key column
|
||||||
|
-- conflicting with the existing row.
|
||||||
|
INSERT INTO products VALUES(1, 'product_1', 1);
|
||||||
|
|
||||||
|
ALTER TABLE products DROP CONSTRAINT p_key;
|
||||||
|
INSERT INTO products VALUES(1, 'product_1', 1);
|
||||||
|
|
||||||
|
-- Can not create constraint since it conflicts with the existing data
|
||||||
|
ALTER TABLE products ADD CONSTRAINT p_key PRIMARY KEY(product_no);
|
||||||
|
|
||||||
|
DROP TABLE products;
|
||||||
|
|
||||||
|
-- Check "PRIMARY KEY CONSTRAINT" with reference table
|
||||||
|
CREATE TABLE products_ref (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT create_reference_table('products_ref');
|
||||||
|
|
||||||
|
-- Can add PRIMARY KEY to any column
|
||||||
|
ALTER TABLE products_ref ADD CONSTRAINT p_key PRIMARY KEY(name);
|
||||||
|
ALTER TABLE products_ref DROP CONSTRAINT p_key;
|
||||||
|
ALTER TABLE products_ref ADD CONSTRAINT p_key PRIMARY KEY(product_no);
|
||||||
|
|
||||||
|
INSERT INTO products_ref VALUES(1, 'product_1', 1);
|
||||||
|
|
||||||
|
-- Should error out, since we are trying to add new row having a value on p_key column
|
||||||
|
-- conflicting with the existing row.
|
||||||
|
INSERT INTO products_ref VALUES(1, 'product_1', 1);
|
||||||
|
|
||||||
|
DROP TABLE products_ref;
|
||||||
|
|
||||||
|
-- Check "PRIMARY KEY CONSTRAINT" on append table
|
||||||
|
CREATE TABLE products_append (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT create_distributed_table('products_append', 'product_no', 'append');
|
||||||
|
|
||||||
|
-- Can only add primary key constraint on distribution column (or group
|
||||||
|
-- of columns including distribution column)
|
||||||
|
-- Command below should error out since 'name' is not a distribution column
|
||||||
|
ALTER TABLE products_append ADD CONSTRAINT p_key_name PRIMARY KEY(name);
|
||||||
|
ALTER TABLE products_append ADD CONSTRAINT p_key PRIMARY KEY(product_no);
|
||||||
|
|
||||||
|
--- Error out since first and third rows have the same product_no
|
||||||
|
\COPY products_append FROM STDIN DELIMITER AS ',';
|
||||||
|
1, Product_1, 10
|
||||||
|
2, Product_2, 15
|
||||||
|
1, Product_3, 8
|
||||||
|
\.
|
||||||
|
|
||||||
|
DROP TABLE products_append;
|
||||||
|
|
||||||
|
|
||||||
|
-- Check "UNIQUE CONSTRAINT"
|
||||||
|
CREATE TABLE unique_test_table(id int, name varchar(20));
|
||||||
|
SELECT create_distributed_table('unique_test_table', 'id');
|
||||||
|
|
||||||
|
-- Can only add unique constraint on distribution column (or group
|
||||||
|
-- of columns including distribution column)
|
||||||
|
-- Command below should error out since 'name' is not a distribution column
|
||||||
|
ALTER TABLE unique_test_table ADD CONSTRAINT unn_name UNIQUE(name);
|
||||||
|
ALTER TABLE unique_test_table ADD CONSTRAINT unn_id UNIQUE(id);
|
||||||
|
|
||||||
|
-- Error out, since table can not have two rows with same id.
|
||||||
|
INSERT INTO unique_test_table VALUES(1, 'Ahmet');
|
||||||
|
INSERT INTO unique_test_table VALUES(1, 'Mehmet');
|
||||||
|
|
||||||
|
ALTER TABLE unique_test_table DROP CONSTRAINT unn_id;
|
||||||
|
|
||||||
|
-- Insert row which will conflict with the next unique constraint command
|
||||||
|
INSERT INTO unique_test_table VALUES(1, 'Mehmet');
|
||||||
|
|
||||||
|
-- Can not create constraint since it conflicts with the existing data
|
||||||
|
ALTER TABLE unique_test_table ADD CONSTRAINT unn_id UNIQUE(id);
|
||||||
|
|
||||||
|
-- Can create unique constraint over multiple columns which must include
|
||||||
|
-- distribution column
|
||||||
|
ALTER TABLE unique_test_table ADD CONSTRAINT unn_id_name UNIQUE(id, name);
|
||||||
|
|
||||||
|
-- Error out, since tables can not have two rows with same id and name.
|
||||||
|
INSERT INTO unique_test_table VALUES(1, 'Mehmet');
|
||||||
|
DROP TABLE unique_test_table;
|
||||||
|
|
||||||
|
-- Check "UNIQUE CONSTRAINT" with reference table
|
||||||
|
CREATE TABLE unique_test_table_ref(id int, name varchar(20));
|
||||||
|
SELECT create_reference_table('unique_test_table_ref');
|
||||||
|
|
||||||
|
-- We can add unique constraint on any column with reference tables
|
||||||
|
ALTER TABLE unique_test_table_ref ADD CONSTRAINT unn_name UNIQUE(name);
|
||||||
|
ALTER TABLE unique_test_table_ref ADD CONSTRAINT unn_id UNIQUE(id);
|
||||||
|
|
||||||
|
-- Error out. Since the table can not have two rows with the same id.
|
||||||
|
INSERT INTO unique_test_table_ref VALUES(1, 'Ahmet');
|
||||||
|
INSERT INTO unique_test_table_ref VALUES(1, 'Mehmet');
|
||||||
|
|
||||||
|
-- We can add unique constraint with multiple columns
|
||||||
|
ALTER TABLE unique_test_table_ref DROP CONSTRAINT unn_id;
|
||||||
|
ALTER TABLE unique_test_table_ref ADD CONSTRAINT unn_id_name UNIQUE(id,name);
|
||||||
|
|
||||||
|
-- Error out, since two rows can not have the same id or name.
|
||||||
|
INSERT INTO unique_test_table_ref VALUES(1, 'Mehmet');
|
||||||
|
|
||||||
|
DROP TABLE unique_test_table_ref;
|
||||||
|
|
||||||
|
-- Check "UNIQUE CONSTRAINT" with append table
|
||||||
|
CREATE TABLE unique_test_table_append(id int, name varchar(20));
|
||||||
|
SELECT create_distributed_table('unique_test_table_append', 'id', 'append');
|
||||||
|
|
||||||
|
-- Can only add unique constraint on distribution column (or group
|
||||||
|
-- of columns including distribution column)
|
||||||
|
-- Command below should error out since 'name' is not a distribution column
|
||||||
|
ALTER TABLE unique_test_table_append ADD CONSTRAINT unn_name UNIQUE(name);
|
||||||
|
ALTER TABLE unique_test_table_append ADD CONSTRAINT unn_id UNIQUE(id);
|
||||||
|
|
||||||
|
-- Error out. Table can not have two rows with the same id.
|
||||||
|
\COPY unique_test_table_append FROM STDIN DELIMITER AS ',';
|
||||||
|
1, Product_1
|
||||||
|
2, Product_2
|
||||||
|
1, Product_3
|
||||||
|
\.
|
||||||
|
|
||||||
|
DROP TABLE unique_test_table_append;
|
||||||
|
|
||||||
|
-- Check "CHECK CONSTRAINT"
|
||||||
|
CREATE TABLE products (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric,
|
||||||
|
discounted_price numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT create_distributed_table('products', 'product_no');
|
||||||
|
|
||||||
|
-- Can add column and table check constraints
|
||||||
|
ALTER TABLE products ADD CONSTRAINT p_check CHECK(price > 0);
|
||||||
|
ALTER TABLE products ADD CONSTRAINT p_multi_check CHECK(price > discounted_price);
|
||||||
|
|
||||||
|
-- First and third queries will error out, because of conflicts with p_check and
|
||||||
|
-- p_multi_check, respectively.
|
||||||
|
INSERT INTO products VALUES(1, 'product_1', -1, -2);
|
||||||
|
INSERT INTO products VALUES(1, 'product_1', 5, 3);
|
||||||
|
INSERT INTO products VALUES(1, 'product_1', 2, 3);
|
||||||
|
|
||||||
|
DROP TABLE products;
|
||||||
|
|
||||||
|
-- Check "CHECK CONSTRAINT" with reference table
|
||||||
|
CREATE TABLE products_ref (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric,
|
||||||
|
discounted_price numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT create_reference_table('products_ref');
|
||||||
|
|
||||||
|
-- Can add column and table check constraints
|
||||||
|
ALTER TABLE products_ref ADD CONSTRAINT p_check CHECK(price > 0);
|
||||||
|
ALTER TABLE products_ref ADD CONSTRAINT p_multi_check CHECK(price > discounted_price);
|
||||||
|
|
||||||
|
-- First and third queries will error out, because of conflicts with p_check and
|
||||||
|
-- p_multi_check, respectively.
|
||||||
|
INSERT INTO products_ref VALUES(1, 'product_1', -1, -2);
|
||||||
|
INSERT INTO products_ref VALUES(1, 'product_1', 5, 3);
|
||||||
|
INSERT INTO products_ref VALUES(1, 'product_1', 2, 3);
|
||||||
|
|
||||||
|
DROP TABLE products_ref;
|
||||||
|
|
||||||
|
-- Check "CHECK CONSTRAINT" with append table
|
||||||
|
CREATE TABLE products_append (
|
||||||
|
product_no int,
|
||||||
|
name varchar(20),
|
||||||
|
price int,
|
||||||
|
discounted_price int
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT create_distributed_table('products_append', 'product_no', 'append');
|
||||||
|
|
||||||
|
-- Can add column and table check constraints
|
||||||
|
ALTER TABLE products_append ADD CONSTRAINT p_check CHECK(price > 0);
|
||||||
|
ALTER TABLE products_append ADD CONSTRAINT p_multi_check CHECK(price > discounted_price);
|
||||||
|
|
||||||
|
-- Error out,since the third row conflicting with the p_multi_check
|
||||||
|
\COPY products_append FROM STDIN DELIMITER AS ',';
|
||||||
|
1, Product_1, 10, 5
|
||||||
|
2, Product_2, 15, 8
|
||||||
|
1, Product_3, 8, 10
|
||||||
|
\.
|
||||||
|
|
||||||
|
DROP TABLE products_append;
|
||||||
|
|
||||||
|
|
||||||
|
-- Check "EXCLUSION CONSTRAINT"
|
||||||
|
CREATE TABLE products (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT create_distributed_table('products', 'product_no');
|
||||||
|
|
||||||
|
-- Can only add exclusion constraint on distribution column (or group of columns
|
||||||
|
-- including distribution column)
|
||||||
|
-- Command below should error out since 'name' is not a distribution column
|
||||||
|
ALTER TABLE products ADD CONSTRAINT exc_name EXCLUDE USING btree (name with =);
|
||||||
|
|
||||||
|
-- We can add composite exclusion
|
||||||
|
ALTER TABLE products ADD CONSTRAINT exc_pno_name EXCLUDE USING btree (product_no with =, name with =);
|
||||||
|
|
||||||
|
-- 4th command will error out since it conflicts with exc_pno_name constraint
|
||||||
|
INSERT INTO products VALUES(1,'product_1', 5);
|
||||||
|
INSERT INTO products VALUES(1,'product_2', 10);
|
||||||
|
INSERT INTO products VALUES(2,'product_2', 5);
|
||||||
|
INSERT INTO products VALUES(2,'product_2', 5);
|
||||||
|
|
||||||
|
DROP TABLE products;
|
||||||
|
|
||||||
|
-- Check "EXCLUSION CONSTRAINT" with reference table
|
||||||
|
CREATE TABLE products_ref (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT create_reference_table('products_ref');
|
||||||
|
|
||||||
|
-- We can add exclusion constraint on any column
|
||||||
|
ALTER TABLE products_ref ADD CONSTRAINT exc_name EXCLUDE USING btree (name with =);
|
||||||
|
|
||||||
|
-- We can add composite exclusion because none of pair of rows are conflicting
|
||||||
|
ALTER TABLE products_ref ADD CONSTRAINT exc_pno_name EXCLUDE USING btree (product_no with =, name with =);
|
||||||
|
|
||||||
|
-- Third insertion will error out, since it has the same name with second insertion
|
||||||
|
INSERT INTO products_ref VALUES(1,'product_1', 5);
|
||||||
|
INSERT INTO products_ref VALUES(1,'product_2', 10);
|
||||||
|
INSERT INTO products_ref VALUES(2,'product_2', 5);
|
||||||
|
|
||||||
|
DROP TABLE products_ref;
|
||||||
|
|
||||||
|
-- Check "EXCLUSION CONSTRAINT" with append table
|
||||||
|
CREATE TABLE products_append (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT create_distributed_table('products_append', 'product_no','append');
|
||||||
|
|
||||||
|
-- Can only add exclusion constraint on distribution column (or group of column
|
||||||
|
-- including distribution column)
|
||||||
|
-- Command below should error out since 'name' is not a distribution column
|
||||||
|
ALTER TABLE products_append ADD CONSTRAINT exc_name EXCLUDE USING btree (name with =);
|
||||||
|
ALTER TABLE products_append ADD CONSTRAINT exc_pno_name EXCLUDE USING btree (product_no with =, name with =);
|
||||||
|
|
||||||
|
-- Error out since first and third can not pass the exclusion check.
|
||||||
|
\COPY products_append FROM STDIN DELIMITER AS ',';
|
||||||
|
1, Product_1, 10
|
||||||
|
1, Product_2, 15
|
||||||
|
1, Product_1, 8
|
||||||
|
\.
|
||||||
|
|
||||||
|
DROP TABLE products_append;
|
||||||
|
|
||||||
|
-- Check "NOT NULL"
|
||||||
|
CREATE TABLE products (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT create_distributed_table('products', 'product_no');
|
||||||
|
|
||||||
|
ALTER TABLE products ALTER COLUMN name SET NOT NULL;
|
||||||
|
|
||||||
|
-- Insertions will error out since both product_no and name can not have NULL value
|
||||||
|
INSERT INTO products VALUES(1,NULL,5);
|
||||||
|
INSERT INTO products VALUES(NULL,'product_1', 5);
|
||||||
|
|
||||||
|
DROP TABLE products;
|
||||||
|
|
||||||
|
-- Check "NOT NULL" with reference table
|
||||||
|
CREATE TABLE products_ref (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT create_reference_table('products_ref');
|
||||||
|
|
||||||
|
ALTER TABLE products_ref ALTER COLUMN name SET NOT NULL;
|
||||||
|
|
||||||
|
-- Insertions will error out since both product_no and name can not have NULL value
|
||||||
|
INSERT INTO products_ref VALUES(1,NULL,5);
|
||||||
|
INSERT INTO products_ref VALUES(NULL,'product_1', 5);
|
||||||
|
|
||||||
|
DROP TABLE products_ref;
|
||||||
|
|
||||||
|
-- Check "NOT NULL" with append table
|
||||||
|
CREATE TABLE products_append (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT create_distributed_table('products_append', 'product_no', 'append');
|
||||||
|
|
||||||
|
ALTER TABLE products_append ALTER COLUMN name SET NOT NULL;
|
||||||
|
|
||||||
|
-- Error out since name and product_no columns can not handle NULL value.
|
||||||
|
\COPY products_append FROM STDIN DELIMITER AS ',';
|
||||||
|
1, \N, 10
|
||||||
|
\N, Product_2, 15
|
||||||
|
1, Product_1, 8
|
||||||
|
\.
|
||||||
|
|
||||||
|
DROP TABLE products_append;
|
||||||
|
|
||||||
|
|
||||||
|
-- Tests for ADD CONSTRAINT is not only subcommand
|
||||||
|
CREATE TABLE products (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT create_distributed_table('products', 'product_no');
|
||||||
|
|
||||||
|
-- Should error out since add constraint is not the single subcommand
|
||||||
|
ALTER TABLE products ADD CONSTRAINT unn_1 UNIQUE(product_no, price), ADD CONSTRAINT unn_2 UNIQUE(product_no, name);
|
||||||
|
|
||||||
|
-- Tests for constraints without name
|
||||||
|
-- Commands below should error out since constraints do not have the name
|
||||||
|
ALTER TABLE products ADD UNIQUE(product_no);
|
||||||
|
ALTER TABLE products ADD PRIMARY KEY(product_no);
|
||||||
|
ALTER TABLE products ADD CHECK(product_no <> 0);
|
||||||
|
ALTER TABLE products ADD EXCLUDE USING btree (product_no with =);
|
||||||
|
|
||||||
|
DROP TABLE products;
|
||||||
|
|
||||||
|
|
||||||
|
-- Tests with transactions
|
||||||
|
CREATE TABLE products (
|
||||||
|
product_no integer,
|
||||||
|
name text,
|
||||||
|
price numeric,
|
||||||
|
discounted_price numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT create_distributed_table('products', 'product_no');
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO products VALUES(1,'product_1', 5);
|
||||||
|
|
||||||
|
-- Should error out since conflicts with the above single-shard data modification command.
|
||||||
|
ALTER TABLE products ADD CONSTRAINT unn_pno UNIQUE(product_no);
|
||||||
|
ROLLBACK;
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
-- Add constraints
|
||||||
|
ALTER TABLE products ADD CONSTRAINT unn_pno UNIQUE(product_no);
|
||||||
|
ALTER TABLE products ADD CONSTRAINT check_price CHECK(price > discounted_price);
|
||||||
|
ALTER TABLE products ALTER COLUMN product_no SET NOT NULL;
|
||||||
|
ALTER TABLE products ADD CONSTRAINT p_key_product PRIMARY KEY(product_no);
|
||||||
|
|
||||||
|
-- Single shard DML command can't be located in the same transaction with above commands.
|
||||||
|
INSERT INTO products VALUES(1,'product_1', 10, 8);
|
||||||
|
ROLLBACK;
|
||||||
|
|
||||||
|
-- There should be no constraint on master and worker(s)
|
||||||
|
\d products
|
||||||
|
|
||||||
|
\c - - - :worker_1_port
|
||||||
|
|
||||||
|
\d products_1450199
|
||||||
|
|
||||||
|
\c - - - :master_port
|
||||||
|
|
||||||
|
-- Tests to check the effect of rollback
|
||||||
|
BEGIN;
|
||||||
|
-- Add constraints (which will be rollbacked)
|
||||||
|
ALTER TABLE products ADD CONSTRAINT unn_pno UNIQUE(product_no);
|
||||||
|
ALTER TABLE products ADD CONSTRAINT check_price CHECK(price > discounted_price);
|
||||||
|
ALTER TABLE products ADD CONSTRAINT p_key_product PRIMARY KEY(product_no);
|
||||||
|
ROLLBACK;
|
||||||
|
|
||||||
|
-- There should be no constraint on master and worker(s)
|
||||||
|
\d products
|
||||||
|
|
||||||
|
\c - - - :worker_1_port
|
||||||
|
|
||||||
|
\d products_1450199
|
||||||
|
|
||||||
|
\c - - - :master_port
|
||||||
|
DROP TABLE products;
|
|
@ -220,7 +220,7 @@ FROM
|
||||||
WHERE
|
WHERE
|
||||||
li.l_quantity > 25
|
li.l_quantity > 25
|
||||||
ORDER BY
|
ORDER BY
|
||||||
li.l_quantity
|
li.l_quantity, li.l_partkey, o.o_custkey
|
||||||
LIMIT 10 OFFSET 20;
|
LIMIT 10 OFFSET 20;
|
||||||
|
|
||||||
RESET client_min_messages;
|
RESET client_min_messages;
|
||||||
|
|
|
@ -535,7 +535,9 @@ FROM
|
||||||
NATURAL JOIN pg_dist_shard
|
NATURAL JOIN pg_dist_shard
|
||||||
NATURAL JOIN pg_dist_shard_placement
|
NATURAL JOIN pg_dist_shard_placement
|
||||||
WHERE
|
WHERE
|
||||||
logicalrelid = 'mx_ref'::regclass;
|
logicalrelid = 'mx_ref'::regclass
|
||||||
|
ORDER BY
|
||||||
|
nodeport;
|
||||||
|
|
||||||
SELECT shardid AS ref_table_shardid FROM pg_dist_shard WHERE logicalrelid='mx_ref'::regclass \gset
|
SELECT shardid AS ref_table_shardid FROM pg_dist_shard WHERE logicalrelid='mx_ref'::regclass \gset
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue