mirror of https://github.com/citusdata/citus.git
Skip execution of ALTER TABLE constraint checks on the coordinator
parent
cdd3b15ac8
commit
a9e6d06a98
|
@ -1093,7 +1093,7 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement)
|
||||||
* for two things for practial purpose for not doing the same checks
|
* for two things for practial purpose for not doing the same checks
|
||||||
* twice:
|
* twice:
|
||||||
* (a) For any command, decide and return whether we should
|
* (a) For any command, decide and return whether we should
|
||||||
* run the command in sequntial mode
|
* run the command in sequential mode
|
||||||
* (b) For commands in a transaction block, set the transaction local
|
* (b) For commands in a transaction block, set the transaction local
|
||||||
* multi-shard modify mode to sequential when necessary
|
* multi-shard modify mode to sequential when necessary
|
||||||
*
|
*
|
||||||
|
@ -1185,17 +1185,6 @@ SetupExecutionModeForAlterTable(Oid relationId, AlterTableCmd *command)
|
||||||
{
|
{
|
||||||
executeSequentially = true;
|
executeSequentially = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Postgres performs additional selects when creating constraints
|
|
||||||
* on partitioned tables. We need to set execution mode to
|
|
||||||
* sequential for the select query so that ddl connections
|
|
||||||
* we open does not fail due to previous select.
|
|
||||||
*/
|
|
||||||
if (executeSequentially && PartitionedTable(relationId))
|
|
||||||
{
|
|
||||||
SetLocalMultiShardModifyModeToSequential();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (alterTableType == AT_DetachPartition || alterTableType == AT_AttachPartition)
|
else if (alterTableType == AT_DetachPartition || alterTableType == AT_AttachPartition)
|
||||||
|
@ -1204,7 +1193,6 @@ SetupExecutionModeForAlterTable(Oid relationId, AlterTableCmd *command)
|
||||||
if (HasForeignKeyToReferenceTable(relationId))
|
if (HasForeignKeyToReferenceTable(relationId))
|
||||||
{
|
{
|
||||||
executeSequentially = true;
|
executeSequentially = true;
|
||||||
SetLocalMultiShardModifyModeToSequential();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,7 @@
|
||||||
|
|
||||||
bool EnableDDLPropagation = true; /* ddl propagation is enabled */
|
bool EnableDDLPropagation = true; /* ddl propagation is enabled */
|
||||||
static bool shouldInvalidateForeignKeyGraph = false;
|
static bool shouldInvalidateForeignKeyGraph = false;
|
||||||
|
static int activeAlterTables = 0;
|
||||||
|
|
||||||
|
|
||||||
/* Local functions forward declarations for helper functions */
|
/* Local functions forward declarations for helper functions */
|
||||||
|
@ -419,8 +420,32 @@ multi_ProcessUtility(PlannedStmt *pstmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
pstmt->utilityStmt = parsetree;
|
pstmt->utilityStmt = parsetree;
|
||||||
standard_ProcessUtility(pstmt, queryString, context,
|
|
||||||
params, queryEnv, dest, completionTag);
|
PG_TRY();
|
||||||
|
{
|
||||||
|
if (IsA(parsetree, AlterTableStmt))
|
||||||
|
{
|
||||||
|
activeAlterTables++;
|
||||||
|
}
|
||||||
|
|
||||||
|
standard_ProcessUtility(pstmt, queryString, context,
|
||||||
|
params, queryEnv, dest, completionTag);
|
||||||
|
|
||||||
|
if (IsA(parsetree, AlterTableStmt))
|
||||||
|
{
|
||||||
|
activeAlterTables--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
if (IsA(parsetree, AlterTableStmt))
|
||||||
|
{
|
||||||
|
activeAlterTables--;
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We only process CREATE TABLE ... PARTITION OF commands in the function below
|
* We only process CREATE TABLE ... PARTITION OF commands in the function below
|
||||||
|
@ -749,3 +774,14 @@ DDLTaskList(Oid relationId, const char *commandString)
|
||||||
|
|
||||||
return taskList;
|
return taskList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AlterTableInProgress returns true if we're processing an ALTER TABLE command
|
||||||
|
* right now.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
AlterTableInProgress(void)
|
||||||
|
{
|
||||||
|
return activeAlterTables > 0;
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "distributed/citus_custom_scan.h"
|
#include "distributed/citus_custom_scan.h"
|
||||||
#include "distributed/commands/multi_copy.h"
|
#include "distributed/commands/multi_copy.h"
|
||||||
|
#include "distributed/commands/utility_hook.h"
|
||||||
#include "distributed/insert_select_executor.h"
|
#include "distributed/insert_select_executor.h"
|
||||||
#include "distributed/insert_select_planner.h"
|
#include "distributed/insert_select_planner.h"
|
||||||
#include "distributed/multi_executor.h"
|
#include "distributed/multi_executor.h"
|
||||||
|
@ -51,7 +52,7 @@ bool WritableStandbyCoordinator = false;
|
||||||
static bool IsCitusPlan(Plan *plan);
|
static bool IsCitusPlan(Plan *plan);
|
||||||
static bool IsCitusCustomScan(Plan *plan);
|
static bool IsCitusCustomScan(Plan *plan);
|
||||||
static Relation StubRelation(TupleDesc tupleDescriptor);
|
static Relation StubRelation(TupleDesc tupleDescriptor);
|
||||||
|
static bool AlterTableConstraintCheck(QueryDesc *queryDesc);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CitusExecutorStart is the ExecutorStart_hook that gets called when
|
* CitusExecutorStart is the ExecutorStart_hook that gets called when
|
||||||
|
@ -96,6 +97,47 @@ CitusExecutorStart(QueryDesc *queryDesc, int eflags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CitusExecutorRun is the ExecutorRun_hook that gets called when postgres
|
||||||
|
* executes a query.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
CitusExecutorRun(QueryDesc *queryDesc,
|
||||||
|
ScanDirection direction, uint64 count, bool execute_once)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Disable execution of ALTER TABLE constraint validation queries. These
|
||||||
|
* constraints will be validated in worker nodes, so running these queries
|
||||||
|
* from the coordinator would be redundant.
|
||||||
|
*
|
||||||
|
* For example, ALTER TABLE ... ATTACH PARTITION checks that the new
|
||||||
|
* partition doesn't violate constraints of the parent table, which
|
||||||
|
* might involve running some SELECT queries.
|
||||||
|
*
|
||||||
|
* Ideally we'd completely skip these checks in the coordinator, but we don't
|
||||||
|
* have any means to tell postgres to skip the checks. So the best we can do is
|
||||||
|
* to not execute the queries and return an empty result set, as if this table has
|
||||||
|
* no rows, so no constraints will be violated.
|
||||||
|
*/
|
||||||
|
if (AlterTableConstraintCheck(queryDesc))
|
||||||
|
{
|
||||||
|
EState *estate = queryDesc->estate;
|
||||||
|
DestReceiver *dest = queryDesc->dest;
|
||||||
|
|
||||||
|
estate->es_processed = 0;
|
||||||
|
estate->es_lastoid = InvalidOid;
|
||||||
|
|
||||||
|
/* start and shutdown tuple receiver to simulate empty result */
|
||||||
|
dest->rStartup(queryDesc->dest, CMD_SELECT, queryDesc->tupDesc);
|
||||||
|
dest->rShutdown(dest);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
standard_ExecutorRun(queryDesc, direction, count, execute_once);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* IsCitusPlan returns whether a Plan contains a CustomScan generated by Citus
|
* IsCitusPlan returns whether a Plan contains a CustomScan generated by Citus
|
||||||
* by recursively walking through the plan tree.
|
* by recursively walking through the plan tree.
|
||||||
|
@ -418,3 +460,41 @@ SetLocalMultiShardModifyModeToSequential()
|
||||||
(superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,
|
(superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,
|
||||||
GUC_ACTION_LOCAL, true, 0, false);
|
GUC_ACTION_LOCAL, true, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AlterTableConstraintCheck returns if the given query is an ALTER TABLE
|
||||||
|
* constraint check query.
|
||||||
|
*
|
||||||
|
* Postgres uses SPI to execute these queries. To see examples of how these
|
||||||
|
* constraint check queries look like, see RI_Initial_Check() and RI_Fkey_check().
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
AlterTableConstraintCheck(QueryDesc *queryDesc)
|
||||||
|
{
|
||||||
|
if (!AlterTableInProgress())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These queries are one or more SELECT queries, where postgres checks
|
||||||
|
* their results either for NULL values or existence of a row at all.
|
||||||
|
*/
|
||||||
|
if (queryDesc->plannedstmt->commandType != CMD_SELECT)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* While an ALTER TABLE is in progress, we might do SELECTs on some
|
||||||
|
* catalog tables too. For example, when dropping a column, citus_drop_trigger()
|
||||||
|
* runs some SELECTs on catalog tables. These are not constraint check queries.
|
||||||
|
*/
|
||||||
|
if (!IsCitusPlan(queryDesc->plannedstmt->planTree))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -164,7 +164,9 @@ _PG_init(void)
|
||||||
* (thus as the innermost/last running hook) to be able to do our
|
* (thus as the innermost/last running hook) to be able to do our
|
||||||
* duties. For simplicity insist that all hooks are previously unused.
|
* duties. For simplicity insist that all hooks are previously unused.
|
||||||
*/
|
*/
|
||||||
if (planner_hook != NULL || ProcessUtility_hook != NULL || ExecutorStart_hook != NULL)
|
if (planner_hook != NULL || ProcessUtility_hook != NULL || ExecutorStart_hook !=
|
||||||
|
NULL ||
|
||||||
|
ExecutorRun_hook != NULL)
|
||||||
{
|
{
|
||||||
ereport(ERROR, (errmsg("Citus has to be loaded first"),
|
ereport(ERROR, (errmsg("Citus has to be loaded first"),
|
||||||
errhint("Place citus at the beginning of "
|
errhint("Place citus at the beginning of "
|
||||||
|
@ -209,6 +211,7 @@ _PG_init(void)
|
||||||
set_rel_pathlist_hook = multi_relation_restriction_hook;
|
set_rel_pathlist_hook = multi_relation_restriction_hook;
|
||||||
set_join_pathlist_hook = multi_join_restriction_hook;
|
set_join_pathlist_hook = multi_join_restriction_hook;
|
||||||
ExecutorStart_hook = CitusExecutorStart;
|
ExecutorStart_hook = CitusExecutorStart;
|
||||||
|
ExecutorRun_hook = CitusExecutorRun;
|
||||||
|
|
||||||
/* register hook for error messages */
|
/* register hook for error messages */
|
||||||
emit_log_hook = multi_log_hook;
|
emit_log_hook = multi_log_hook;
|
||||||
|
|
|
@ -45,5 +45,6 @@ extern void CitusProcessUtility(Node *node, const char *queryString,
|
||||||
extern void MarkInvalidateForeignKeyGraph(void);
|
extern void MarkInvalidateForeignKeyGraph(void);
|
||||||
extern void InvalidateForeignKeyGraphForDDL(void);
|
extern void InvalidateForeignKeyGraphForDDL(void);
|
||||||
extern List * DDLTaskList(Oid relationId, const char *commandString);
|
extern List * DDLTaskList(Oid relationId, const char *commandString);
|
||||||
|
extern bool AlterTableInProgress(void);
|
||||||
|
|
||||||
#endif /* MULTI_UTILITY_H */
|
#endif /* MULTI_UTILITY_H */
|
||||||
|
|
|
@ -32,6 +32,8 @@ extern bool WritableStandbyCoordinator;
|
||||||
|
|
||||||
|
|
||||||
extern void CitusExecutorStart(QueryDesc *queryDesc, int eflags);
|
extern void CitusExecutorStart(QueryDesc *queryDesc, int eflags);
|
||||||
|
extern void CitusExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count,
|
||||||
|
bool execute_once);
|
||||||
extern TupleTableSlot * ReturnTupleFromTuplestore(CitusScanState *scanState);
|
extern TupleTableSlot * ReturnTupleFromTuplestore(CitusScanState *scanState);
|
||||||
extern void LoadTuplesIntoTupleStore(CitusScanState *citusScanState, Job *workerJob);
|
extern void LoadTuplesIntoTupleStore(CitusScanState *citusScanState, Job *workerJob);
|
||||||
extern void ReadFileIntoTupleStore(char *fileName, char *copyFormat, TupleDesc
|
extern void ReadFileIntoTupleStore(char *fileName, char *copyFormat, TupleDesc
|
||||||
|
|
|
@ -511,14 +511,14 @@ ERROR: cannot execute DDL on reference relation "referece_table" because there
|
||||||
HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';"
|
HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';"
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
-- case 5.4: Parallel UPDATE on distributed table follow by a related DDL on reference table
|
-- case 5.4: Parallel UPDATE on distributed table follow by a related DDL on reference table
|
||||||
-- FIXME: Can we do better?
|
|
||||||
BEGIN;
|
BEGIN;
|
||||||
UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15;
|
UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15;
|
||||||
ALTER TABLE referece_table ALTER COLUMN id SET DATA TYPE smallint;
|
ALTER TABLE referece_table ALTER COLUMN id SET DATA TYPE smallint;
|
||||||
DEBUG: rewriting table "referece_table"
|
DEBUG: rewriting table "referece_table"
|
||||||
DEBUG: building index "referece_table_pkey" on table "referece_table"
|
DEBUG: building index "referece_table_pkey" on table "referece_table"
|
||||||
DEBUG: validating foreign key constraint "fkey"
|
DEBUG: validating foreign key constraint "fkey"
|
||||||
ERROR: cannot perform DDL on placement 2380001, which has been read over multiple connections
|
ERROR: cannot execute DDL on reference relation "referece_table" because there was a parallel DML access to distributed relation "on_update_fkey_table" in the same transaction
|
||||||
|
HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';"
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
-- case 6:1: Unrelated parallel DDL on distributed table followed by SELECT on ref. table
|
-- case 6:1: Unrelated parallel DDL on distributed table followed by SELECT on ref. table
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
|
|
@ -511,14 +511,14 @@ ERROR: cannot execute DDL on reference relation "referece_table" because there
|
||||||
HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';"
|
HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';"
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
-- case 5.4: Parallel UPDATE on distributed table follow by a related DDL on reference table
|
-- case 5.4: Parallel UPDATE on distributed table follow by a related DDL on reference table
|
||||||
-- FIXME: Can we do better?
|
|
||||||
BEGIN;
|
BEGIN;
|
||||||
UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15;
|
UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15;
|
||||||
ALTER TABLE referece_table ALTER COLUMN id SET DATA TYPE smallint;
|
ALTER TABLE referece_table ALTER COLUMN id SET DATA TYPE smallint;
|
||||||
DEBUG: rewriting table "referece_table"
|
DEBUG: rewriting table "referece_table"
|
||||||
DEBUG: building index "referece_table_pkey" on table "referece_table" serially
|
DEBUG: building index "referece_table_pkey" on table "referece_table" serially
|
||||||
DEBUG: validating foreign key constraint "fkey"
|
DEBUG: validating foreign key constraint "fkey"
|
||||||
ERROR: cannot perform DDL on placement 2380001, which has been read over multiple connections
|
ERROR: cannot execute DDL on reference relation "referece_table" because there was a parallel DML access to distributed relation "on_update_fkey_table" in the same transaction
|
||||||
|
HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';"
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
-- case 6:1: Unrelated parallel DDL on distributed table followed by SELECT on ref. table
|
-- case 6:1: Unrelated parallel DDL on distributed table followed by SELECT on ref. table
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
|
|
@ -1936,8 +1936,9 @@ INSERT INTO partitioning_test_2010 VALUES (1, '2010-02-01');
|
||||||
-- This should fail because of foreign key constraint violation
|
-- This should fail because of foreign key constraint violation
|
||||||
ALTER TABLE partitioning_test ATTACH PARTITION partitioning_test_2010
|
ALTER TABLE partitioning_test ATTACH PARTITION partitioning_test_2010
|
||||||
FOR VALUES FROM ('2010-01-01') TO ('2011-01-01');
|
FOR VALUES FROM ('2010-01-01') TO ('2011-01-01');
|
||||||
ERROR: insert or update on table "partitioning_test_2010" violates foreign key constraint "partitioning_reference_fkey"
|
ERROR: insert or update on table "partitioning_test_2010_1660191" violates foreign key constraint "partitioning_reference_fkey_1660179"
|
||||||
DETAIL: Key (id)=(1) is not present in table "reference_table".
|
DETAIL: Key (id)=(1) is not present in table "reference_table_1660177".
|
||||||
|
CONTEXT: while executing command on localhost:57637
|
||||||
-- Truncate, so attaching again won't fail
|
-- Truncate, so attaching again won't fail
|
||||||
TRUNCATE partitioning_test_2010;
|
TRUNCATE partitioning_test_2010;
|
||||||
-- Attach a table which already has the same constraint
|
-- Attach a table which already has the same constraint
|
||||||
|
|
|
@ -303,7 +303,6 @@ BEGIN;
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
|
|
||||||
-- case 5.4: Parallel UPDATE on distributed table follow by a related DDL on reference table
|
-- case 5.4: Parallel UPDATE on distributed table follow by a related DDL on reference table
|
||||||
-- FIXME: Can we do better?
|
|
||||||
BEGIN;
|
BEGIN;
|
||||||
UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15;
|
UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15;
|
||||||
ALTER TABLE referece_table ALTER COLUMN id SET DATA TYPE smallint;
|
ALTER TABLE referece_table ALTER COLUMN id SET DATA TYPE smallint;
|
||||||
|
|
Loading…
Reference in New Issue