mirror of https://github.com/citusdata/citus.git
Merge pull request #1608 from citusdata/sequential_multi_row_insert
Execute multi-row INSERTs sequentiallypull/1600/head
commit
ad1fbbe186
|
@ -58,17 +58,16 @@ static CustomExecMethods TaskTrackerCustomExecMethods = {
|
||||||
.ExplainCustomScan = CitusExplainScan
|
.ExplainCustomScan = CitusExplainScan
|
||||||
};
|
};
|
||||||
|
|
||||||
static CustomExecMethods RouterSingleModifyCustomExecMethods = {
|
static CustomExecMethods RouterSequentialModifyCustomExecMethods = {
|
||||||
.CustomName = "RouterSingleModifyScan",
|
.CustomName = "RouterSequentialModifyScan",
|
||||||
.BeginCustomScan = CitusModifyBeginScan,
|
.BeginCustomScan = CitusModifyBeginScan,
|
||||||
.ExecCustomScan = RouterSingleModifyExecScan,
|
.ExecCustomScan = RouterSequentialModifyExecScan,
|
||||||
.EndCustomScan = CitusEndScan,
|
.EndCustomScan = CitusEndScan,
|
||||||
.ReScanCustomScan = CitusReScan,
|
.ReScanCustomScan = CitusReScan,
|
||||||
.ExplainCustomScan = CitusExplainScan
|
.ExplainCustomScan = CitusExplainScan
|
||||||
};
|
};
|
||||||
|
|
||||||
/* not static to enable reference by multi-modify logic in router execution */
|
static CustomExecMethods RouterMultiModifyCustomExecMethods = {
|
||||||
CustomExecMethods RouterMultiModifyCustomExecMethods = {
|
|
||||||
.CustomName = "RouterMultiModifyScan",
|
.CustomName = "RouterMultiModifyScan",
|
||||||
.BeginCustomScan = CitusModifyBeginScan,
|
.BeginCustomScan = CitusModifyBeginScan,
|
||||||
.ExecCustomScan = RouterMultiModifyExecScan,
|
.ExecCustomScan = RouterMultiModifyExecScan,
|
||||||
|
@ -100,6 +99,7 @@ static CustomExecMethods CoordinatorInsertSelectCustomExecMethods = {
|
||||||
static void PrepareMasterJobDirectory(Job *workerJob);
|
static void PrepareMasterJobDirectory(Job *workerJob);
|
||||||
static void LoadTuplesIntoTupleStore(CitusScanState *citusScanState, Job *workerJob);
|
static void LoadTuplesIntoTupleStore(CitusScanState *citusScanState, Job *workerJob);
|
||||||
static Relation StubRelation(TupleDesc tupleDescriptor);
|
static Relation StubRelation(TupleDesc tupleDescriptor);
|
||||||
|
static bool IsMultiRowInsert(Query *query);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -165,7 +165,7 @@ RouterCreateScan(CustomScan *scan)
|
||||||
{
|
{
|
||||||
if (isModificationQuery)
|
if (isModificationQuery)
|
||||||
{
|
{
|
||||||
scanState->customScanState.methods = &RouterSingleModifyCustomExecMethods;
|
scanState->customScanState.methods = &RouterSequentialModifyCustomExecMethods;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -175,13 +175,59 @@ RouterCreateScan(CustomScan *scan)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Assert(isModificationQuery);
|
Assert(isModificationQuery);
|
||||||
|
|
||||||
|
if (IsMultiRowInsert(workerJob->jobQuery))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Multi-row INSERT is executed sequentially instead of using
|
||||||
|
* parallel connections.
|
||||||
|
*/
|
||||||
|
scanState->customScanState.methods = &RouterSequentialModifyCustomExecMethods;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
scanState->customScanState.methods = &RouterMultiModifyCustomExecMethods;
|
scanState->customScanState.methods = &RouterMultiModifyCustomExecMethods;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (Node *) scanState;
|
return (Node *) scanState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IsMultiRowInsert returns whether the given query is a multi-row INSERT.
|
||||||
|
*
|
||||||
|
* It does this by determining whether the query is an INSERT that has an
|
||||||
|
* RTE_VALUES. Single-row INSERTs will have their RTE_VALUES optimised away
|
||||||
|
* in transformInsertStmt, and instead use the target list.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
IsMultiRowInsert(Query *query)
|
||||||
|
{
|
||||||
|
ListCell *rteCell = NULL;
|
||||||
|
bool hasValuesRTE = false;
|
||||||
|
|
||||||
|
CmdType commandType = query->commandType;
|
||||||
|
if (commandType != CMD_INSERT)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(rteCell, query->rtable)
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rteCell);
|
||||||
|
|
||||||
|
if (rte->rtekind == RTE_VALUES)
|
||||||
|
{
|
||||||
|
hasValuesRTE = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasValuesRTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CoordinatorInsertSelectCrateScan creates the scan state for executing
|
* CoordinatorInsertSelectCrateScan creates the scan state for executing
|
||||||
* INSERT..SELECT into a distributed table via the coordinator.
|
* INSERT..SELECT into a distributed table via the coordinator.
|
||||||
|
|
|
@ -80,7 +80,7 @@ static void AcquireMetadataLocks(List *taskList);
|
||||||
static ShardPlacementAccess * CreatePlacementAccess(ShardPlacement *placement,
|
static ShardPlacementAccess * CreatePlacementAccess(ShardPlacement *placement,
|
||||||
ShardPlacementAccessType accessType);
|
ShardPlacementAccessType accessType);
|
||||||
static void ExecuteSingleModifyTask(CitusScanState *scanState, Task *task,
|
static void ExecuteSingleModifyTask(CitusScanState *scanState, Task *task,
|
||||||
bool expectResults);
|
bool multipleTasks, bool expectResults);
|
||||||
static void ExecuteSingleSelectTask(CitusScanState *scanState, Task *task);
|
static void ExecuteSingleSelectTask(CitusScanState *scanState, Task *task);
|
||||||
static List * GetModifyConnections(Task *task, bool markCritical);
|
static List * GetModifyConnections(Task *task, bool markCritical);
|
||||||
static void ExecuteMultipleTasks(CitusScanState *scanState, List *taskList,
|
static void ExecuteMultipleTasks(CitusScanState *scanState, List *taskList,
|
||||||
|
@ -427,11 +427,6 @@ CitusModifyBeginScan(CustomScanState *node, EState *estate, int eflags)
|
||||||
RaiseDeferredError(planningError, ERROR);
|
RaiseDeferredError(planningError, ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list_length(taskList) > 1)
|
|
||||||
{
|
|
||||||
node->methods = &RouterMultiModifyCustomExecMethods;
|
|
||||||
}
|
|
||||||
|
|
||||||
workerJob->taskList = taskList;
|
workerJob->taskList = taskList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,11 +448,11 @@ CitusModifyBeginScan(CustomScanState *node, EState *estate, int eflags)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* RouterSingleModifyExecScan executes a single modification query on a
|
* RouterSequentialModifyExecScan executes 0 or more modifications on a
|
||||||
* distributed plan and returns results if there is any.
|
* distributed table sequentially and returns results if there are any.
|
||||||
*/
|
*/
|
||||||
TupleTableSlot *
|
TupleTableSlot *
|
||||||
RouterSingleModifyExecScan(CustomScanState *node)
|
RouterSequentialModifyExecScan(CustomScanState *node)
|
||||||
{
|
{
|
||||||
CitusScanState *scanState = (CitusScanState *) node;
|
CitusScanState *scanState = (CitusScanState *) node;
|
||||||
TupleTableSlot *resultSlot = NULL;
|
TupleTableSlot *resultSlot = NULL;
|
||||||
|
@ -468,12 +463,25 @@ RouterSingleModifyExecScan(CustomScanState *node)
|
||||||
bool hasReturning = multiPlan->hasReturning;
|
bool hasReturning = multiPlan->hasReturning;
|
||||||
Job *workerJob = multiPlan->workerJob;
|
Job *workerJob = multiPlan->workerJob;
|
||||||
List *taskList = workerJob->taskList;
|
List *taskList = workerJob->taskList;
|
||||||
|
ListCell *taskCell = NULL;
|
||||||
|
bool multipleTasks = list_length(taskList) > 1;
|
||||||
|
|
||||||
if (list_length(taskList) > 0)
|
/*
|
||||||
|
* We could naturally handle function-based transactions (i.e. those using
|
||||||
|
* PL/pgSQL or similar) by checking the type of queryDesc->dest, but some
|
||||||
|
* customers already use functions that touch multiple shards from within
|
||||||
|
* a function, so we'll ignore functions for now.
|
||||||
|
*/
|
||||||
|
if (IsTransactionBlock() || multipleTasks)
|
||||||
{
|
{
|
||||||
Task *task = (Task *) linitial(taskList);
|
BeginOrContinueCoordinatedTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
ExecuteSingleModifyTask(scanState, task, hasReturning);
|
foreach(taskCell, taskList)
|
||||||
|
{
|
||||||
|
Task *task = (Task *) lfirst(taskCell);
|
||||||
|
|
||||||
|
ExecuteSingleModifyTask(scanState, task, multipleTasks, hasReturning);
|
||||||
}
|
}
|
||||||
|
|
||||||
scanState->finishedRemoteScan = true;
|
scanState->finishedRemoteScan = true;
|
||||||
|
@ -682,7 +690,8 @@ CreatePlacementAccess(ShardPlacement *placement, ShardPlacementAccessType access
|
||||||
* framework), or errors out (failed on all placements).
|
* framework), or errors out (failed on all placements).
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ExecuteSingleModifyTask(CitusScanState *scanState, Task *task, bool expectResults)
|
ExecuteSingleModifyTask(CitusScanState *scanState, Task *task, bool multipleTasks,
|
||||||
|
bool expectResults)
|
||||||
{
|
{
|
||||||
CmdType operation = scanState->multiPlan->operation;
|
CmdType operation = scanState->multiPlan->operation;
|
||||||
EState *executorState = scanState->customScanState.ss.ps.state;
|
EState *executorState = scanState->customScanState.ss.ps.state;
|
||||||
|
@ -713,17 +722,6 @@ ExecuteSingleModifyTask(CitusScanState *scanState, Task *task, bool expectResult
|
||||||
CoordinatedTransactionUse2PC();
|
CoordinatedTransactionUse2PC();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* We could naturally handle function-based transactions (i.e. those using
|
|
||||||
* PL/pgSQL or similar) by checking the type of queryDesc->dest, but some
|
|
||||||
* customers already use functions that touch multiple shards from within
|
|
||||||
* a function, so we'll ignore functions for now.
|
|
||||||
*/
|
|
||||||
if (IsTransactionBlock())
|
|
||||||
{
|
|
||||||
BeginOrContinueCoordinatedTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get connections required to execute task. This will, if necessary,
|
* Get connections required to execute task. This will, if necessary,
|
||||||
* establish the connection, mark as critical (when modifying reference
|
* establish the connection, mark as critical (when modifying reference
|
||||||
|
@ -775,6 +773,15 @@ ExecuteSingleModifyTask(CitusScanState *scanState, Task *task, bool expectResult
|
||||||
/* if we're running a 2PC, the query should fail on error */
|
/* if we're running a 2PC, the query should fail on error */
|
||||||
failOnError = taskRequiresTwoPhaseCommit;
|
failOnError = taskRequiresTwoPhaseCommit;
|
||||||
|
|
||||||
|
if (multipleTasks && expectResults)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If we have multiple tasks and one fails, we cannot clear
|
||||||
|
* the tuple store and start over. Error out instead.
|
||||||
|
*/
|
||||||
|
failOnError = true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If caller is interested, store query results the first time
|
* If caller is interested, store query results the first time
|
||||||
* through. The output of the query's execution on other shards is
|
* through. The output of the query's execution on other shards is
|
||||||
|
@ -822,7 +829,7 @@ ExecuteSingleModifyTask(CitusScanState *scanState, Task *task, bool expectResult
|
||||||
/* if some placements failed, ensure future statements don't access them */
|
/* if some placements failed, ensure future statements don't access them */
|
||||||
MarkFailedShardPlacements();
|
MarkFailedShardPlacements();
|
||||||
|
|
||||||
executorState->es_processed = affectedTupleCount;
|
executorState->es_processed += affectedTupleCount;
|
||||||
|
|
||||||
if (IsTransactionBlock())
|
if (IsTransactionBlock())
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,8 +28,6 @@ typedef struct CitusScanState
|
||||||
} CitusScanState;
|
} CitusScanState;
|
||||||
|
|
||||||
|
|
||||||
extern CustomExecMethods RouterMultiModifyCustomExecMethods;
|
|
||||||
|
|
||||||
extern Node * RealTimeCreateScan(CustomScan *scan);
|
extern Node * RealTimeCreateScan(CustomScan *scan);
|
||||||
extern Node * TaskTrackerCreateScan(CustomScan *scan);
|
extern Node * TaskTrackerCreateScan(CustomScan *scan);
|
||||||
extern Node * RouterCreateScan(CustomScan *scan);
|
extern Node * RouterCreateScan(CustomScan *scan);
|
||||||
|
|
|
@ -36,7 +36,7 @@ extern bool AllModificationsCommutative;
|
||||||
extern bool EnableDeadlockPrevention;
|
extern bool EnableDeadlockPrevention;
|
||||||
|
|
||||||
extern void CitusModifyBeginScan(CustomScanState *node, EState *estate, int eflags);
|
extern void CitusModifyBeginScan(CustomScanState *node, EState *estate, int eflags);
|
||||||
extern TupleTableSlot * RouterSingleModifyExecScan(CustomScanState *node);
|
extern TupleTableSlot * RouterSequentialModifyExecScan(CustomScanState *node);
|
||||||
extern TupleTableSlot * RouterSelectExecScan(CustomScanState *node);
|
extern TupleTableSlot * RouterSelectExecScan(CustomScanState *node);
|
||||||
extern TupleTableSlot * RouterMultiModifyExecScan(CustomScanState *node);
|
extern TupleTableSlot * RouterMultiModifyExecScan(CustomScanState *node);
|
||||||
|
|
||||||
|
|
|
@ -59,11 +59,10 @@ step s1-multi-insert:
|
||||||
|
|
||||||
step s2-multi-insert-overlap:
|
step s2-multi-insert-overlap:
|
||||||
INSERT INTO test_concurrent_dml VALUES (1), (4);
|
INSERT INTO test_concurrent_dml VALUES (1), (4);
|
||||||
<waiting ...>
|
|
||||||
step s1-commit:
|
step s1-commit:
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
step s2-multi-insert-overlap: <... completed>
|
|
||||||
|
|
||||||
starting permutation: s1-begin s2-begin s1-multi-insert s2-multi-insert s1-commit s2-commit
|
starting permutation: s1-begin s2-begin s1-multi-insert s2-multi-insert s1-commit s2-commit
|
||||||
master_create_worker_shards
|
master_create_worker_shards
|
||||||
|
|
|
@ -221,10 +221,12 @@ INSERT INTO researchers VALUES (10, 6, 'Lamport Leslie');
|
||||||
ERROR: cannot establish a new connection for placement 1200003, since DML has been executed on a connection that is in use
|
ERROR: cannot establish a new connection for placement 1200003, since DML has been executed on a connection that is in use
|
||||||
CONTEXT: COPY researchers, line 2: "10,6,Lesport Lampie"
|
CONTEXT: COPY researchers, line 2: "10,6,Lesport Lampie"
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
-- but it is allowed after a multi-row insert
|
-- COPY cannot be performed after a multi-row INSERT that uses one connection
|
||||||
BEGIN;
|
BEGIN;
|
||||||
INSERT INTO researchers VALUES (2, 1, 'Knuth Donald'), (10, 6, 'Lamport Leslie');
|
INSERT INTO researchers VALUES (2, 1, 'Knuth Donald'), (10, 6, 'Lamport Leslie');
|
||||||
\copy researchers from stdin delimiter ','
|
\copy researchers from stdin delimiter ','
|
||||||
|
ERROR: cannot establish a new connection for placement 1200003, since DML has been executed on a connection that is in use
|
||||||
|
CONTEXT: COPY researchers, line 2: "10,6,Lesport Lampie"
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
-- after a COPY you can modify multiple shards, since they'll use different connections
|
-- after a COPY you can modify multiple shards, since they'll use different connections
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
@ -232,6 +234,11 @@ BEGIN;
|
||||||
INSERT INTO researchers VALUES (2, 1, 'Knuth Donald');
|
INSERT INTO researchers VALUES (2, 1, 'Knuth Donald');
|
||||||
INSERT INTO researchers VALUES (10, 6, 'Lamport Leslie');
|
INSERT INTO researchers VALUES (10, 6, 'Lamport Leslie');
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
|
-- after a COPY you can perform a multi-row INSERT
|
||||||
|
BEGIN;
|
||||||
|
\copy researchers from stdin delimiter ','
|
||||||
|
INSERT INTO researchers VALUES (2, 1, 'Knuth Donald'), (10, 6, 'Lamport Leslie');
|
||||||
|
ROLLBACK;
|
||||||
-- COPY can happen before single row INSERT
|
-- COPY can happen before single row INSERT
|
||||||
BEGIN;
|
BEGIN;
|
||||||
\copy labs from stdin delimiter ','
|
\copy labs from stdin delimiter ','
|
||||||
|
|
|
@ -180,7 +180,7 @@ INSERT INTO researchers VALUES (10, 6, 'Lamport Leslie');
|
||||||
\.
|
\.
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
|
|
||||||
-- but it is allowed after a multi-row insert
|
-- COPY cannot be performed after a multi-row INSERT that uses one connection
|
||||||
BEGIN;
|
BEGIN;
|
||||||
INSERT INTO researchers VALUES (2, 1, 'Knuth Donald'), (10, 6, 'Lamport Leslie');
|
INSERT INTO researchers VALUES (2, 1, 'Knuth Donald'), (10, 6, 'Lamport Leslie');
|
||||||
\copy researchers from stdin delimiter ','
|
\copy researchers from stdin delimiter ','
|
||||||
|
@ -199,6 +199,15 @@ INSERT INTO researchers VALUES (2, 1, 'Knuth Donald');
|
||||||
INSERT INTO researchers VALUES (10, 6, 'Lamport Leslie');
|
INSERT INTO researchers VALUES (10, 6, 'Lamport Leslie');
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
|
|
||||||
|
-- after a COPY you can perform a multi-row INSERT
|
||||||
|
BEGIN;
|
||||||
|
\copy researchers from stdin delimiter ','
|
||||||
|
3,1,Duth Knonald
|
||||||
|
10,6,Lesport Lampie
|
||||||
|
\.
|
||||||
|
INSERT INTO researchers VALUES (2, 1, 'Knuth Donald'), (10, 6, 'Lamport Leslie');
|
||||||
|
ROLLBACK;
|
||||||
|
|
||||||
-- COPY can happen before single row INSERT
|
-- COPY can happen before single row INSERT
|
||||||
BEGIN;
|
BEGIN;
|
||||||
\copy labs from stdin delimiter ','
|
\copy labs from stdin delimiter ','
|
||||||
|
|
Loading…
Reference in New Issue