mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Allow asynchronous execution in more cases.
In commit 27e1f1456
, create_append_plan() only allowed the subplan
created from a given subpath to be executed asynchronously when it was
an async-capable ForeignPath. To extend coverage, this patch handles
cases when the given subpath includes some other Path types as well that
can be omitted in the plan processing, such as a ProjectionPath directly
atop an async-capable ForeignPath, allowing asynchronous execution in
partitioned-scan/partitioned-join queries with non-Var tlist expressions
and more UNION queries.
Andrey Lepikhov and Etsuro Fujita, reviewed by Alexander Pyhalov and
Zhihong Yu.
Discussion: https://postgr.es/m/659c37a8-3e71-0ff2-394c-f04428c76f08%40postgrespro.ru
This commit is contained in:
@ -632,6 +632,7 @@ _copySubqueryScan(const SubqueryScan *from)
|
||||
* copy remainder of node
|
||||
*/
|
||||
COPY_NODE_FIELD(subplan);
|
||||
COPY_SCALAR_FIELD(scanstatus);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
@ -638,6 +638,7 @@ _outSubqueryScan(StringInfo str, const SubqueryScan *node)
|
||||
_outScanInfo(str, (const Scan *) node);
|
||||
|
||||
WRITE_NODE_FIELD(subplan);
|
||||
WRITE_ENUM_FIELD(scanstatus, SubqueryScanStatus);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -2194,6 +2194,7 @@ _readSubqueryScan(void)
|
||||
ReadCommonScan(&local_node->scan);
|
||||
|
||||
READ_NODE_FIELD(subplan);
|
||||
READ_ENUM_FIELD(scanstatus, SubqueryScanStatus);
|
||||
|
||||
READ_DONE();
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ static List *get_gating_quals(PlannerInfo *root, List *quals);
|
||||
static Plan *create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
|
||||
List *gating_quals);
|
||||
static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path);
|
||||
static bool is_async_capable_path(Path *path);
|
||||
static bool mark_async_capable_plan(Plan *plan, Path *path);
|
||||
static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path,
|
||||
int flags);
|
||||
static Plan *create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
|
||||
@ -1110,14 +1110,30 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
|
||||
}
|
||||
|
||||
/*
|
||||
* is_async_capable_path
|
||||
* Check whether a given Path node is async-capable.
|
||||
* mark_async_capable_plan
|
||||
* Check whether a given Path node is async-capable, and if so, mark the
|
||||
* Plan node created from it as such and return true, otherwise return
|
||||
* false.
|
||||
*/
|
||||
static bool
|
||||
is_async_capable_path(Path *path)
|
||||
mark_async_capable_plan(Plan *plan, Path *path)
|
||||
{
|
||||
switch (nodeTag(path))
|
||||
{
|
||||
case T_SubqueryScanPath:
|
||||
{
|
||||
SubqueryScan *scan_plan = (SubqueryScan *) plan;
|
||||
|
||||
/*
|
||||
* If a SubqueryScan node atop of an async-capable plan node
|
||||
* is deletable, consider it as async-capable.
|
||||
*/
|
||||
if (trivial_subqueryscan(scan_plan) &&
|
||||
mark_async_capable_plan(scan_plan->subplan,
|
||||
((SubqueryScanPath *) path)->subpath))
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
case T_ForeignPath:
|
||||
{
|
||||
FdwRoutine *fdwroutine = path->parent->fdwroutine;
|
||||
@ -1125,13 +1141,27 @@ is_async_capable_path(Path *path)
|
||||
Assert(fdwroutine != NULL);
|
||||
if (fdwroutine->IsForeignPathAsyncCapable != NULL &&
|
||||
fdwroutine->IsForeignPathAsyncCapable((ForeignPath *) path))
|
||||
return true;
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case T_ProjectionPath:
|
||||
|
||||
/*
|
||||
* If the generated plan node doesn't include a Result node,
|
||||
* consider it as async-capable if the subpath is async-capable.
|
||||
*/
|
||||
if (!IsA(plan, Result) &&
|
||||
mark_async_capable_plan(plan,
|
||||
((ProjectionPath *) path)->subpath))
|
||||
return true;
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
plan->async_capable = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1294,14 +1324,14 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
|
||||
}
|
||||
}
|
||||
|
||||
subplans = lappend(subplans, subplan);
|
||||
|
||||
/* Check to see if subplan can be executed asynchronously */
|
||||
if (consider_async && is_async_capable_path(subpath))
|
||||
/* If needed, check to see if subplan can be executed asynchronously */
|
||||
if (consider_async && mark_async_capable_plan(subplan, subpath))
|
||||
{
|
||||
subplan->async_capable = true;
|
||||
Assert(subplan->async_capable);
|
||||
++nasyncplans;
|
||||
}
|
||||
|
||||
subplans = lappend(subplans, subplan);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -5598,6 +5628,7 @@ make_subqueryscan(List *qptlist,
|
||||
plan->righttree = NULL;
|
||||
node->scan.scanrelid = scanrelid;
|
||||
node->subplan = subplan;
|
||||
node->scanstatus = SUBQUERY_SCAN_UNKNOWN;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
@ -115,7 +115,6 @@ static Plan *set_indexonlyscan_references(PlannerInfo *root,
|
||||
static Plan *set_subqueryscan_references(PlannerInfo *root,
|
||||
SubqueryScan *plan,
|
||||
int rtoffset);
|
||||
static bool trivial_subqueryscan(SubqueryScan *plan);
|
||||
static Plan *clean_up_removed_plan_level(Plan *parent, Plan *child);
|
||||
static void set_foreignscan_references(PlannerInfo *root,
|
||||
ForeignScan *fscan,
|
||||
@ -1319,14 +1318,26 @@ set_subqueryscan_references(PlannerInfo *root,
|
||||
*
|
||||
* We can delete it if it has no qual to check and the targetlist just
|
||||
* regurgitates the output of the child plan.
|
||||
*
|
||||
* This might be called repeatedly on a SubqueryScan node, so we cache the
|
||||
* result in the SubqueryScan node to avoid repeated computation.
|
||||
*/
|
||||
static bool
|
||||
bool
|
||||
trivial_subqueryscan(SubqueryScan *plan)
|
||||
{
|
||||
int attrno;
|
||||
ListCell *lp,
|
||||
*lc;
|
||||
|
||||
/* We might have detected this already (see mark_async_capable_plan) */
|
||||
if (plan->scanstatus == SUBQUERY_SCAN_TRIVIAL)
|
||||
return true;
|
||||
if (plan->scanstatus == SUBQUERY_SCAN_NONTRIVIAL)
|
||||
return false;
|
||||
Assert(plan->scanstatus == SUBQUERY_SCAN_UNKNOWN);
|
||||
/* Initially, mark the SubqueryScan as non-deletable from the plan tree */
|
||||
plan->scanstatus = SUBQUERY_SCAN_NONTRIVIAL;
|
||||
|
||||
if (plan->scan.plan.qual != NIL)
|
||||
return false;
|
||||
|
||||
@ -1368,6 +1379,9 @@ trivial_subqueryscan(SubqueryScan *plan)
|
||||
attrno++;
|
||||
}
|
||||
|
||||
/* Re-mark the SubqueryScan as deletable from the plan tree */
|
||||
plan->scanstatus = SUBQUERY_SCAN_TRIVIAL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -536,16 +536,28 @@ typedef struct TidRangeScan
|
||||
* relation, we make this a descendant of Scan anyway for code-sharing
|
||||
* purposes.
|
||||
*
|
||||
* SubqueryScanStatus caches the trivial_subqueryscan property of the node.
|
||||
* SUBQUERY_SCAN_UNKNOWN means not yet determined. This is only used during
|
||||
* planning.
|
||||
*
|
||||
* Note: we store the sub-plan in the type-specific subplan field, not in
|
||||
* the generic lefttree field as you might expect. This is because we do
|
||||
* not want plan-tree-traversal routines to recurse into the subplan without
|
||||
* knowing that they are changing Query contexts.
|
||||
* ----------------
|
||||
*/
|
||||
typedef enum SubqueryScanStatus
|
||||
{
|
||||
SUBQUERY_SCAN_UNKNOWN,
|
||||
SUBQUERY_SCAN_TRIVIAL,
|
||||
SUBQUERY_SCAN_NONTRIVIAL
|
||||
} SubqueryScanStatus;
|
||||
|
||||
typedef struct SubqueryScan
|
||||
{
|
||||
Scan scan;
|
||||
Plan *subplan;
|
||||
SubqueryScanStatus scanstatus;
|
||||
} SubqueryScan;
|
||||
|
||||
/* ----------------
|
||||
|
@ -112,6 +112,7 @@ extern bool innerrel_is_unique(PlannerInfo *root,
|
||||
* prototypes for plan/setrefs.c
|
||||
*/
|
||||
extern Plan *set_plan_references(PlannerInfo *root, Plan *plan);
|
||||
extern bool trivial_subqueryscan(SubqueryScan *plan);
|
||||
extern void record_plan_function_dependency(PlannerInfo *root, Oid funcid);
|
||||
extern void record_plan_type_dependency(PlannerInfo *root, Oid typid);
|
||||
extern bool extract_query_dependencies_walker(Node *node, PlannerInfo *root);
|
||||
|
Reference in New Issue
Block a user