aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/transam/parallel.c4
-rw-r--r--src/backend/commands/explain.c14
-rw-r--r--src/backend/nodes/copyfuncs.c1
-rw-r--r--src/backend/nodes/outfuncs.c2
-rw-r--r--src/backend/nodes/readfuncs.c1
-rw-r--r--src/backend/optimizer/plan/createplan.c5
-rw-r--r--src/backend/optimizer/plan/planner.c73
-rw-r--r--src/backend/utils/misc/guc.c24
-rw-r--r--src/backend/utils/misc/postgresql.conf.sample1
-rw-r--r--src/include/nodes/plannodes.h1
-rw-r--r--src/include/nodes/relation.h3
-rw-r--r--src/include/optimizer/planmain.h9
12 files changed, 118 insertions, 20 deletions
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index bf2e691f577..4f91cd0265d 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -22,6 +22,7 @@
#include "libpq/pqformat.h"
#include "libpq/pqmq.h"
#include "miscadmin.h"
+#include "optimizer/planmain.h"
#include "storage/ipc.h"
#include "storage/sinval.h"
#include "storage/spin.h"
@@ -1079,7 +1080,8 @@ ParallelExtensionTrampoline(dsm_segment *seg, shm_toc *toc)
static void
ParallelErrorContext(void *arg)
{
- errcontext("parallel worker, PID %d", *(int32 *) arg);
+ if (force_parallel_mode != FORCE_PARALLEL_REGRESS)
+ errcontext("parallel worker, PID %d", *(int32 *) arg);
}
/*
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 25d8ca075d4..ee13136b7fd 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -23,6 +23,7 @@
#include "foreign/fdwapi.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
+#include "optimizer/planmain.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
#include "tcop/tcopprot.h"
@@ -572,6 +573,7 @@ void
ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
{
Bitmapset *rels_used = NULL;
+ PlanState *ps;
Assert(queryDesc->plannedstmt != NULL);
es->pstmt = queryDesc->plannedstmt;
@@ -580,7 +582,17 @@ ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
es->rtable_names = select_rtable_names_for_explain(es->rtable, rels_used);
es->deparse_cxt = deparse_context_for_plan_rtable(es->rtable,
es->rtable_names);
- ExplainNode(queryDesc->planstate, NIL, NULL, NULL, es);
+
+ /*
+ * Sometimes we mark a Gather node as "invisible", which means that it's
+ * not displayed in EXPLAIN output. The purpose of this is to allow
+ * running regression tests with force_parallel_mode=regress to get the
+ * same results as running the same tests with force_parallel_mode=off.
+ */
+ ps = queryDesc->planstate;
+ if (IsA(ps, GatherState) &&((Gather *) ps->plan)->invisible)
+ ps = outerPlanState(ps);
+ ExplainNode(ps, NIL, NULL, NULL, es);
}
/*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index a8b79fa8c31..e54d1744b0a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -334,6 +334,7 @@ _copyGather(const Gather *from)
*/
COPY_SCALAR_FIELD(num_workers);
COPY_SCALAR_FIELD(single_copy);
+ COPY_SCALAR_FIELD(invisible);
return newnode;
}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index d59b9546543..3e1c3e6be57 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -443,6 +443,7 @@ _outGather(StringInfo str, const Gather *node)
WRITE_INT_FIELD(num_workers);
WRITE_BOOL_FIELD(single_copy);
+ WRITE_BOOL_FIELD(invisible);
}
static void
@@ -1824,6 +1825,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node)
WRITE_BOOL_FIELD(hasRowSecurity);
WRITE_BOOL_FIELD(parallelModeOK);
WRITE_BOOL_FIELD(parallelModeNeeded);
+ WRITE_BOOL_FIELD(wholePlanParallelSafe);
WRITE_BOOL_FIELD(hasForeignJoin);
}
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 6c461513d64..e4d41ee95b2 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -2053,6 +2053,7 @@ _readGather(void)
READ_INT_FIELD(num_workers);
READ_BOOL_FIELD(single_copy);
+ READ_BOOL_FIELD(invisible);
READ_DONE();
}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 54ff7f623d4..6e0db080383 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -212,6 +212,10 @@ create_plan(PlannerInfo *root, Path *best_path)
/* Recursively process the path tree */
plan = create_plan_recurse(root, best_path);
+ /* Update parallel safety information if needed. */
+ if (!best_path->parallel_safe)
+ root->glob->wholePlanParallelSafe = false;
+
/* Check we successfully assigned all NestLoopParams to plan nodes */
if (root->curOuterParams != NIL)
elog(ERROR, "failed to assign all NestLoopParams to plan nodes");
@@ -4829,6 +4833,7 @@ make_gather(List *qptlist,
plan->righttree = NULL;
node->num_workers = nworkers;
node->single_copy = single_copy;
+ node->invisible = false;
return node;
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a09b4b5b479..a3cc27464c1 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -48,10 +48,12 @@
#include "storage/dsm_impl.h"
#include "utils/rel.h"
#include "utils/selfuncs.h"
+#include "utils/syscache.h"
-/* GUC parameter */
+/* GUC parameters */
double cursor_tuple_fraction = DEFAULT_CURSOR_TUPLE_FRACTION;
+int force_parallel_mode = FORCE_PARALLEL_OFF;
/* Hook for plugins to get control in planner() */
planner_hook_type planner_hook = NULL;
@@ -230,25 +232,31 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
!has_parallel_hazard((Node *) parse, true);
/*
- * glob->parallelModeOK should tell us whether it's necessary to impose
- * the parallel mode restrictions, but we don't actually want to impose
- * them unless we choose a parallel plan, so that people who mislabel
- * their functions but don't use parallelism anyway aren't harmed.
- * However, it's useful for testing purposes to be able to force the
- * restrictions to be imposed whenever a parallel plan is actually chosen
- * or not.
+ * glob->parallelModeNeeded should tell us whether it's necessary to
+ * impose the parallel mode restrictions, but we don't actually want to
+ * impose them unless we choose a parallel plan, so that people who
+ * mislabel their functions but don't use parallelism anyway aren't
+ * harmed. But when force_parallel_mode is set, we enable the restrictions
+ * whenever possible for testing purposes.
*
- * (It's been suggested that we should always impose these restrictions
- * whenever glob->parallelModeOK is true, so that it's easier to notice
- * incorrectly-labeled functions sooner. That might be the right thing to
- * do, but for now I've taken this approach. We could also control this
- * with a GUC.)
+ * glob->wholePlanParallelSafe should tell us whether it's OK to stick a
+ * Gather node on top of the entire plan. However, it only needs to be
+ * accurate when force_parallel_mode is 'on' or 'regress', so we don't
+ * bother doing the work otherwise. The value we set here is just a
+ * preliminary guess; it may get changed from true to false later, but
+ * not visca versa.
*/
-#ifdef FORCE_PARALLEL_MODE
- glob->parallelModeNeeded = glob->parallelModeOK;
-#else
- glob->parallelModeNeeded = false;
-#endif
+ if (force_parallel_mode == FORCE_PARALLEL_OFF || !glob->parallelModeOK)
+ {
+ glob->parallelModeNeeded = false;
+ glob->wholePlanParallelSafe = false; /* either false or don't care */
+ }
+ else
+ {
+ glob->parallelModeNeeded = true;
+ glob->wholePlanParallelSafe =
+ !has_parallel_hazard((Node *) parse, false);
+ }
/* Determine what fraction of the plan is likely to be scanned */
if (cursorOptions & CURSOR_OPT_FAST_PLAN)
@@ -293,6 +301,35 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
}
/*
+ * At present, we don't copy subplans to workers. The presence of a
+ * subplan in one part of the plan doesn't preclude the use of parallelism
+ * in some other part of the plan, but it does preclude the possibility of
+ * regarding the entire plan parallel-safe.
+ */
+ if (glob->subplans != NULL)
+ glob->wholePlanParallelSafe = false;
+
+ /*
+ * Optionally add a Gather node for testing purposes, provided this is
+ * actually a safe thing to do.
+ */
+ if (glob->wholePlanParallelSafe &&
+ force_parallel_mode != FORCE_PARALLEL_OFF)
+ {
+ Gather *gather = makeNode(Gather);
+
+ gather->plan.targetlist = top_plan->targetlist;
+ gather->plan.qual = NIL;
+ gather->plan.lefttree = top_plan;
+ gather->plan.righttree = NULL;
+ gather->num_workers = 1;
+ gather->single_copy = true;
+ gather->invisible = (force_parallel_mode == FORCE_PARALLEL_REGRESS);
+ root->glob->parallelModeNeeded = true;
+ top_plan = &gather->plan;
+ }
+
+ /*
* If any Params were generated, run through the plan tree and compute
* each plan node's extParam/allParam sets. Ideally we'd merge this into
* set_plan_references' tree traversal, but for now it has to be separate
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 66c479141f3..31a69cac723 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -379,6 +379,19 @@ static const struct config_enum_entry huge_pages_options[] = {
{NULL, 0, false}
};
+static const struct config_enum_entry force_parallel_mode_options[] = {
+ {"off", FORCE_PARALLEL_OFF, false},
+ {"on", FORCE_PARALLEL_ON, false},
+ {"regress", FORCE_PARALLEL_REGRESS, false},
+ {"true", FORCE_PARALLEL_ON, true},
+ {"false", FORCE_PARALLEL_OFF, true},
+ {"yes", FORCE_PARALLEL_ON, true},
+ {"no", FORCE_PARALLEL_OFF, true},
+ {"1", FORCE_PARALLEL_ON, true},
+ {"0", FORCE_PARALLEL_OFF, true},
+ {NULL, 0, false}
+};
+
/*
* Options for enum values stored in other modules
*/
@@ -863,6 +876,7 @@ static struct config_bool ConfigureNamesBool[] =
true,
NULL, NULL, NULL
},
+
{
{"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
gettext_noop("Enables genetic query optimization."),
@@ -3672,6 +3686,16 @@ static struct config_enum ConfigureNamesEnum[] =
NULL, NULL, NULL
},
+ {
+ {"force_parallel_mode", PGC_USERSET, QUERY_TUNING_OTHER,
+ gettext_noop("Forces use of parallel query facilities."),
+ gettext_noop("If possible, run query using a parallel worker and with parallel restrictions.")
+ },
+ &force_parallel_mode,
+ FORCE_PARALLEL_OFF, force_parallel_mode_options,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 029114fc22d..09b2003dbe2 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -313,6 +313,7 @@
#from_collapse_limit = 8
#join_collapse_limit = 8 # 1 disables collapsing of explicit
# JOIN clauses
+#force_parallel_mode = off
#------------------------------------------------------------------------------
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 55d6bbe8f0e..ae224cfa314 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -775,6 +775,7 @@ typedef struct Gather
Plan plan;
int num_workers;
bool single_copy;
+ bool invisible; /* suppress EXPLAIN display (for testing)? */
} Gather;
/* ----------------
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 595438cb24d..96198aeec18 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -108,6 +108,9 @@ typedef struct PlannerGlobal
bool parallelModeOK; /* parallel mode potentially OK? */
bool parallelModeNeeded; /* parallel mode actually required? */
+
+ bool wholePlanParallelSafe; /* is the entire plan parallel safe? */
+
bool hasForeignJoin; /* does have a pushed down foreign join */
} PlannerGlobal;
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 7ae73676e8e..eaa642bc57e 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -17,9 +17,18 @@
#include "nodes/plannodes.h"
#include "nodes/relation.h"
+/* possible values for force_parallel_mode */
+typedef enum
+{
+ FORCE_PARALLEL_OFF,
+ FORCE_PARALLEL_ON,
+ FORCE_PARALLEL_REGRESS
+} ForceParallelMode;
+
/* GUC parameters */
#define DEFAULT_CURSOR_TUPLE_FRACTION 0.1
extern double cursor_tuple_fraction;
+extern int force_parallel_mode;
/* query_planner callback to compute query_pathkeys */
typedef void (*query_pathkeys_callback) (PlannerInfo *root, void *extra);