mirror of https://github.com/citusdata/citus.git
prevent master_remove_node(coordinator) when coordinator has reference table replicas involved in a fkey const. with a local table
parent
8cec216af1
commit
badf59852c
|
@ -40,6 +40,7 @@ static void ForeignConstraintFindDistKeys(HeapTuple pgConstraintTuple,
|
||||||
Var *referencedDistColumn,
|
Var *referencedDistColumn,
|
||||||
int *referencingAttrIndex,
|
int *referencingAttrIndex,
|
||||||
int *referencedAttrIndex);
|
int *referencedAttrIndex);
|
||||||
|
static Oid GetCoordinatorLocalTableHavingFKeyWithReferenceTable(Oid referenceTableOid);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ConstraintIsAForeignKeyToReferenceTable checks if the given constraint is a
|
* ConstraintIsAForeignKeyToReferenceTable checks if the given constraint is a
|
||||||
|
@ -597,6 +598,116 @@ ErrorIfUnsupportedAlterAddDropFKeyBetweenReferecenceAndLocalTable(Oid referencin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ErrorIfCoordinatorHasLocalTableReferencingReferenceTable errors out if we
|
||||||
|
* if coordinator has reference table replica for given reference table and if
|
||||||
|
* it is involved in a foreign key constraint with a coordinator local table.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ErrorIfCoordinatorHasLocalTableReferencingReferenceTable(Oid referenceTableOid)
|
||||||
|
{
|
||||||
|
Assert(OidIsValid(referenceTableOid));
|
||||||
|
|
||||||
|
Oid localTableOid = GetCoordinatorLocalTableHavingFKeyWithReferenceTable(
|
||||||
|
referenceTableOid);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is a local table involved in a foreign key constraint with
|
||||||
|
* reference table with referenceTableOid
|
||||||
|
*/
|
||||||
|
if (OidIsValid(localTableOid))
|
||||||
|
{
|
||||||
|
const char *localTableName = get_rel_name(localTableOid);
|
||||||
|
const char *referenceTableName = get_rel_name(referenceTableOid);
|
||||||
|
|
||||||
|
Assert(localTableName != NULL && referenceTableName != NULL);
|
||||||
|
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
|
||||||
|
errmsg(
|
||||||
|
"cannot remove reference table placements from coordinator"),
|
||||||
|
errdetail(
|
||||||
|
"Local table \"%s\" is involved in a foreign key constraint with "
|
||||||
|
"reference table \"%s\", which has replica(s) in coordinator",
|
||||||
|
localTableName, referenceTableName),
|
||||||
|
errhint(
|
||||||
|
"DROP foreign key constraint between them or drop referenced "
|
||||||
|
"table with DROP ... CASCADE.")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetCoordinatorLocalTableHavingFKeyWithReferenceTable returns OID of the
|
||||||
|
* local table that is involved in a foreign key constraint with the reference
|
||||||
|
* table with referenceTableOid.
|
||||||
|
* It does that by scanning pg_constraint for foreign key constraints.
|
||||||
|
*
|
||||||
|
* If there does not exist such a foreign key constraint, returns InvalidOid.
|
||||||
|
* If there exists more than one such a foreign key constraint, we return the
|
||||||
|
* first local table we encounter while scanning pg_constraint
|
||||||
|
*/
|
||||||
|
static Oid
|
||||||
|
GetCoordinatorLocalTableHavingFKeyWithReferenceTable(Oid referenceTableOid)
|
||||||
|
{
|
||||||
|
Oid referenceTableShardOid = GetOnlyShardOidOfReferenceTable(referenceTableOid);
|
||||||
|
|
||||||
|
/* early exit if we could not hit to a healthy shard relation */
|
||||||
|
if (!OidIsValid(referenceTableShardOid))
|
||||||
|
{
|
||||||
|
return InvalidOid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scanKeyCount = 1;
|
||||||
|
ScanKeyData scanKey[1];
|
||||||
|
|
||||||
|
Relation pgConstraint = heap_open(ConstraintRelationId, AccessShareLock);
|
||||||
|
ScanKeyInit(&scanKey[0], Anum_pg_constraint_contype, BTEqualStrategyNumber, F_CHAREQ,
|
||||||
|
CharGetDatum(CONSTRAINT_FOREIGN));
|
||||||
|
SysScanDesc scanDescriptor =
|
||||||
|
systable_beginscan(pgConstraint, InvalidOid, false, NULL, scanKeyCount, scanKey);
|
||||||
|
|
||||||
|
HeapTuple heapTuple = systable_getnext(scanDescriptor);
|
||||||
|
|
||||||
|
while (HeapTupleIsValid(heapTuple))
|
||||||
|
{
|
||||||
|
Form_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);
|
||||||
|
|
||||||
|
if (constraintForm->confrelid == referenceTableShardOid)
|
||||||
|
{
|
||||||
|
Oid referencingTableId = constraintForm->conrelid;
|
||||||
|
|
||||||
|
if (!IsCitusTable(referencingTableId))
|
||||||
|
{
|
||||||
|
return referencingTableId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (constraintForm->conrelid == referenceTableShardOid)
|
||||||
|
{
|
||||||
|
Oid referencedTableId = constraintForm->confrelid;
|
||||||
|
|
||||||
|
if (!IsCitusTable(referencedTableId))
|
||||||
|
{
|
||||||
|
return referencedTableId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this is not such a relation from/to the given relation, we should
|
||||||
|
* simply skip.
|
||||||
|
*/
|
||||||
|
heapTuple = systable_getnext(scanDescriptor);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clean up scan and close system catalog */
|
||||||
|
|
||||||
|
systable_endscan(scanDescriptor);
|
||||||
|
heap_close(pgConstraint, AccessShareLock);
|
||||||
|
|
||||||
|
return InvalidOid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ColumnAppearsInForeignKeyToReferenceTable checks if there is a foreign key
|
* ColumnAppearsInForeignKeyToReferenceTable checks if there is a foreign key
|
||||||
* constraint from/to a reference table on the given column. We iterate
|
* constraint from/to a reference table on the given column. We iterate
|
||||||
|
|
|
@ -511,7 +511,7 @@ LoadShardIntervalList(Oid relationId)
|
||||||
Oid
|
Oid
|
||||||
GetOnlyShardOidOfReferenceTable(Oid referenceTableOid)
|
GetOnlyShardOidOfReferenceTable(Oid referenceTableOid)
|
||||||
{
|
{
|
||||||
DistTableCacheEntry *cacheEntry = DistributedTableCacheEntry(referenceTableOid);
|
const CitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(referenceTableOid);
|
||||||
|
|
||||||
/* assert that it is a "valid" "reference table" */
|
/* assert that it is a "valid" "reference table" */
|
||||||
Assert(cacheEntry != NULL && cacheEntry->shardIntervalArrayLength == 1);
|
Assert(cacheEntry != NULL && cacheEntry->shardIntervalArrayLength == 1);
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "access/heapam.h"
|
#include "access/heapam.h"
|
||||||
#include "access/htup_details.h"
|
#include "access/htup_details.h"
|
||||||
#include "access/genam.h"
|
#include "access/genam.h"
|
||||||
|
#include "catalog/pg_constraint.h"
|
||||||
#include "distributed/colocation_utils.h"
|
#include "distributed/colocation_utils.h"
|
||||||
#include "distributed/commands.h"
|
#include "distributed/commands.h"
|
||||||
#include "distributed/listutils.h"
|
#include "distributed/listutils.h"
|
||||||
|
@ -391,15 +392,18 @@ CreateReferenceTableColocationId()
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DeleteAllReferenceTablePlacementsFromNodeGroup function iterates over list of reference
|
* DeleteAllReferenceTablePlacementsFromNodeGroup function iterates over list of
|
||||||
* tables and deletes all reference table placements from pg_dist_placement table
|
* reference tables and deletes all reference table placements from pg_dist_placement
|
||||||
* for given group.
|
* table for given group.
|
||||||
|
*
|
||||||
|
* Error out if we are to remove coordinator if it has reference table replicas and
|
||||||
|
* if any of those replicas is involved in a foreign constraint with a coordinator
|
||||||
|
* local table.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
DeleteAllReferenceTablePlacementsFromNodeGroup(int32 groupId)
|
DeleteAllReferenceTablePlacementsFromNodeGroup(int32 groupId)
|
||||||
{
|
{
|
||||||
List *referenceTableList = ReferenceTableOidList();
|
List *referenceTableList = ReferenceTableOidList();
|
||||||
List *referenceShardIntervalList = NIL;
|
|
||||||
|
|
||||||
/* if there are no reference tables, we do not need to do anything */
|
/* if there are no reference tables, we do not need to do anything */
|
||||||
if (list_length(referenceTableList) == 0)
|
if (list_length(referenceTableList) == 0)
|
||||||
|
@ -414,7 +418,8 @@ DeleteAllReferenceTablePlacementsFromNodeGroup(int32 groupId)
|
||||||
referenceTableList = SortList(referenceTableList, CompareOids);
|
referenceTableList = SortList(referenceTableList, CompareOids);
|
||||||
if (ClusterHasKnownMetadataWorkers())
|
if (ClusterHasKnownMetadataWorkers())
|
||||||
{
|
{
|
||||||
referenceShardIntervalList = GetSortedReferenceShardIntervals(referenceTableList);
|
List *referenceShardIntervalList = GetSortedReferenceShardIntervals(
|
||||||
|
referenceTableList);
|
||||||
|
|
||||||
BlockWritesToShardList(referenceShardIntervalList);
|
BlockWritesToShardList(referenceShardIntervalList);
|
||||||
}
|
}
|
||||||
|
@ -432,12 +437,24 @@ DeleteAllReferenceTablePlacementsFromNodeGroup(int32 groupId)
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupShardPlacement *placement = (GroupShardPlacement *) linitial(placements);
|
GroupShardPlacement *placement = (GroupShardPlacement *) linitial(placements);
|
||||||
|
uint64 referenceTableShardId = placement->shardId;
|
||||||
|
|
||||||
LockShardDistributionMetadata(placement->shardId, ExclusiveLock);
|
LockShardDistributionMetadata(referenceTableShardId, ExclusiveLock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Error out if we are to remove coordinator and if reference table with
|
||||||
|
* referenceTableId is involved in a foreign constraint with a coordinator
|
||||||
|
* local table.
|
||||||
|
*/
|
||||||
|
if (groupId == COORDINATOR_GROUP_ID)
|
||||||
|
{
|
||||||
|
ErrorIfCoordinatorHasLocalTableReferencingReferenceTable(referenceTableId);
|
||||||
|
}
|
||||||
|
|
||||||
DeleteShardPlacementRow(placement->placementId);
|
DeleteShardPlacementRow(placement->placementId);
|
||||||
|
|
||||||
resetStringInfo(deletePlacementCommand);
|
resetStringInfo(deletePlacementCommand);
|
||||||
|
|
||||||
appendStringInfo(deletePlacementCommand,
|
appendStringInfo(deletePlacementCommand,
|
||||||
"DELETE FROM pg_dist_placement WHERE placementid = "
|
"DELETE FROM pg_dist_placement WHERE placementid = "
|
||||||
UINT64_FORMAT,
|
UINT64_FORMAT,
|
||||||
|
|
|
@ -107,6 +107,8 @@ extern void ErrorIfUnsupportedAlterAddDropFKeyBetweenReferecenceAndLocalTable(Oi
|
||||||
alterTableType,
|
alterTableType,
|
||||||
Constraint *
|
Constraint *
|
||||||
constraint);
|
constraint);
|
||||||
|
extern void ErrorIfCoordinatorHasLocalTableReferencingReferenceTable(Oid
|
||||||
|
referenceTableOid);
|
||||||
extern bool ColumnAppearsInForeignKeyToReferenceTable(char *columnName, Oid
|
extern bool ColumnAppearsInForeignKeyToReferenceTable(char *columnName, Oid
|
||||||
relationId);
|
relationId);
|
||||||
extern List * GetTableForeignConstraintCommands(Oid relationId);
|
extern List * GetTableForeignConstraintCommands(Oid relationId);
|
||||||
|
|
Loading…
Reference in New Issue