aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/execMain.c30
-rw-r--r--src/backend/executor/functions.c3
-rw-r--r--src/backend/executor/spi.c32
3 files changed, 49 insertions, 16 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index df4da3faa97..26793eecf48 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -147,8 +147,20 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
/*
* If the transaction is read-only, we need to check if any writes are
* planned to non-temporary tables. EXPLAIN is considered read-only.
+ *
+ * Don't allow writes in parallel mode. Supporting UPDATE and DELETE would
+ * require (a) storing the combocid hash in shared memory, rather than
+ * synchronizing it just once at the start of parallelism, and (b) an
+ * alternative to heap_update()'s reliance on xmax for mutual exclusion.
+ * INSERT may have no such troubles, but we forbid it to simplify the
+ * checks.
+ *
+ * We have lower-level defenses in CommandCounterIncrement and elsewhere
+ * against performing unsafe operations in parallel mode, but this gives
+ * a more user-friendly error message.
*/
- if (XactReadOnly && !(eflags & EXEC_FLAG_EXPLAIN_ONLY))
+ if ((XactReadOnly || IsInParallelMode()) &&
+ !(eflags & EXEC_FLAG_EXPLAIN_ONLY))
ExecCheckXactReadOnly(queryDesc->plannedstmt);
/*
@@ -691,18 +703,23 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
}
/*
- * Check that the query does not imply any writes to non-temp tables.
+ * Check that the query does not imply any writes to non-temp tables;
+ * unless we're in parallel mode, in which case don't even allow writes
+ * to temp tables.
*
* Note: in a Hot Standby slave this would need to reject writes to temp
- * tables as well; but an HS slave can't have created any temp tables
- * in the first place, so no need to check that.
+ * tables just as we do in parallel mode; but an HS slave can't have created
+ * any temp tables in the first place, so no need to check that.
*/
static void
ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
{
ListCell *l;
- /* Fail if write permissions are requested on any non-temp table */
+ /*
+ * Fail if write permissions are requested in parallel mode for
+ * table (temp or non-temp), otherwise fail for any non-temp table.
+ */
foreach(l, plannedstmt->rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
@@ -718,6 +735,9 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
PreventCommandIfReadOnly(CreateCommandTag((Node *) plannedstmt));
}
+
+ if (plannedstmt->commandType != CMD_SELECT || plannedstmt->hasModifyingCTE)
+ PreventCommandIfParallelMode(CreateCommandTag((Node *) plannedstmt));
}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 6c3eff7c584..ce49c471d49 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -513,6 +513,9 @@ init_execution_state(List *queryTree_list,
errmsg("%s is not allowed in a non-volatile function",
CreateCommandTag(stmt))));
+ if (IsInParallelMode() && !CommandIsReadOnly(stmt))
+ PreventCommandIfParallelMode(CreateCommandTag(stmt));
+
/* OK, build the execution_state for this query */
newes = (execution_state *) palloc(sizeof(execution_state));
if (preves)
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index b3c05025bc0..557d153f2ab 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -23,6 +23,7 @@
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/spi_priv.h"
+#include "miscadmin.h"
#include "tcop/pquery.h"
#include "tcop/utility.h"
#include "utils/builtins.h"
@@ -1322,13 +1323,14 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
}
/*
- * If told to be read-only, we'd better check for read-only queries. This
- * can't be done earlier because we need to look at the finished, planned
- * queries. (In particular, we don't want to do it between GetCachedPlan
- * and PortalDefineQuery, because throwing an error between those steps
- * would result in leaking our plancache refcount.)
+ * If told to be read-only, or in parallel mode, verify that this query
+ * is in fact read-only. This can't be done earlier because we need to
+ * look at the finished, planned queries. (In particular, we don't want
+ * to do it between GetCachedPlan and PortalDefineQuery, because throwing
+ * an error between those steps would result in leaking our plancache
+ * refcount.)
*/
- if (read_only)
+ if (read_only || IsInParallelMode())
{
ListCell *lc;
@@ -1337,11 +1339,16 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
Node *pstmt = (Node *) lfirst(lc);
if (!CommandIsReadOnly(pstmt))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- /* translator: %s is a SQL statement name */
- errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag(pstmt))));
+ {
+ if (read_only)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ /* translator: %s is a SQL statement name */
+ errmsg("%s is not allowed in a non-volatile function",
+ CreateCommandTag(pstmt))));
+ else
+ PreventCommandIfParallelMode(CreateCommandTag(pstmt));
+ }
}
}
@@ -2129,6 +2136,9 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
errmsg("%s is not allowed in a non-volatile function",
CreateCommandTag(stmt))));
+ if (IsInParallelMode() && !CommandIsReadOnly(stmt))
+ PreventCommandIfParallelMode(CreateCommandTag(stmt));
+
/*
* If not read-only mode, advance the command counter before each
* command and update the snapshot.