</listitem>
</varlistentry>
+ <varlistentry id="guc-force-parallel-mode" xreflabel="force_parallel_mode">
+ <term><varname>force_parallel_mode</varname> (<type>boolean</type>)
+ <indexterm>
+ <primary><varname>force_parallel_mode</> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ This setting is intended for testing purposes only. It has
+ several different but related effects. First, it forces parallel
+ mode restrictions to be enforced for all queries except those which
+ the query planner believes would fail to execute properly with
+ those restrictions in place. If an error occurs when this option is
+ used, some functions used by the query may need to be marked
+ <literal>PARALLEL UNSAFE</literal>.
+ </para>
+
+ <para>
+ Second, if the finished plan appears to be safe to run within a
+ parallel worker process, then the query planner will do so. This
+ provides a further test of the parallel query facilities and may
+ reveal additional problems with function markings.
+ </para>
+
+ <para>
+ Third, if a parallel worker prints an error or other message while
+ this option is enabled, the error context indicating that the message
+ originated from a parallel worker will be suppressed. This makes
+ it possible to run the same set of regression tests with and without
+ this option enabled and expect to get the same results.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
</sect1>
#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"
static void
ParallelErrorContext(void *arg)
{
- errcontext("parallel worker, PID %d", *(int32 *) arg);
+ if (!force_parallel_mode)
+ errcontext("parallel worker, PID %d", *(int32 *) arg);
}
/*
ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
{
Bitmapset *rels_used = NULL;
+ PlanState *ps;
Assert(queryDesc->plannedstmt != NULL);
es->pstmt = queryDesc->plannedstmt;
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);
+ /*
+ * XXX. Just for testing purposes, suppress the display of a toplevel
+ * gather node, so that we can run the regression tests with Gather
+ * nodes forcibly inserted without getting test failures due to different
+ * EXPLAIN output.
+ */
+ ps = queryDesc->planstate;
+ if (IsA(ps, GatherState))
+ ps = outerPlanState(ps);
+ ExplainNode(ps, NIL, NULL, NULL, es);
}
/*
WRITE_BOOL_FIELD(hasRowSecurity);
WRITE_BOOL_FIELD(parallelModeOK);
WRITE_BOOL_FIELD(parallelModeNeeded);
+ WRITE_BOOL_FIELD(wholePlanParallelSafe);
WRITE_BOOL_FIELD(hasForeignJoin);
}
/* 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");
#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;
+bool force_parallel_mode = false;
/* Hook for plugins to get control in planner() */
planner_hook_type planner_hook = NULL;
!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 set, 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 || !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);
+ }
+
+ /*
+ * We might flip this from true to false later on during planning, but
+ * never the other direction.
+ */
+ glob->wholePlanParallelSafe = glob->parallelModeOK;
/* Determine what fraction of the plan is likely to be scanned */
if (cursorOptions & CURSOR_OPT_FAST_PLAN)
top_plan = materialize_finished_plan(top_plan);
}
+ /*
+ * 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)
+ {
+ 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;
+ 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
true,
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,
+ false,
+ NULL, NULL, NULL
+ },
+
{
{"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
gettext_noop("Enables genetic query optimization."),
#from_collapse_limit = 8
#join_collapse_limit = 8 # 1 disables collapsing of explicit
# JOIN clauses
+#force_parallel_mode = off
#------------------------------------------------------------------------------
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;
/* GUC parameters */
#define DEFAULT_CURSOR_TUPLE_FRACTION 0.1
extern double cursor_tuple_fraction;
+extern bool force_parallel_mode;
/* query_planner callback to compute query_pathkeys */
typedef void (*query_pathkeys_callback) (PlannerInfo *root, void *extra);