mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-10-02 20:26:51 +02:00
If a LIMIT is applied to a UNION ALL query, plan each UNION arm as
if the limit were directly applied to it. This does not actually add a LIMIT plan node to the generated subqueries --- that would be useless overhead --- but it does cause the planner to prefer fast- start plans when the limit is small. After an idea from Phil Endecott.
This commit is contained in:
parent
39cee73889
commit
3b167a4099
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.188 2005/06/05 22:32:56 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.189 2005/06/10 02:21:04 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -58,6 +58,8 @@ static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
|
|||||||
static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
|
static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
|
||||||
static Plan *inheritance_planner(PlannerInfo *root, List *inheritlist);
|
static Plan *inheritance_planner(PlannerInfo *root, List *inheritlist);
|
||||||
static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction);
|
static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction);
|
||||||
|
static double adjust_tuple_fraction_for_limit(PlannerInfo *root,
|
||||||
|
double tuple_fraction);
|
||||||
static bool choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
|
static bool choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
|
||||||
Path *cheapest_path, Path *sorted_path,
|
Path *cheapest_path, Path *sorted_path,
|
||||||
List *sort_pathkeys, List *group_pathkeys,
|
List *sort_pathkeys, List *group_pathkeys,
|
||||||
@ -648,15 +650,30 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
|
|||||||
List *current_pathkeys;
|
List *current_pathkeys;
|
||||||
List *sort_pathkeys;
|
List *sort_pathkeys;
|
||||||
|
|
||||||
|
/* Tweak caller-supplied tuple_fraction if have LIMIT */
|
||||||
|
if (parse->limitCount != NULL)
|
||||||
|
tuple_fraction = adjust_tuple_fraction_for_limit(root, tuple_fraction);
|
||||||
|
|
||||||
if (parse->setOperations)
|
if (parse->setOperations)
|
||||||
{
|
{
|
||||||
List *set_sortclauses;
|
List *set_sortclauses;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there's a top-level ORDER BY, assume we have to fetch all
|
||||||
|
* the tuples. This might seem too simplistic given all the
|
||||||
|
* hackery below to possibly avoid the sort ... but a nonzero
|
||||||
|
* tuple_fraction is only of use to plan_set_operations() when
|
||||||
|
* the setop is UNION ALL, and the result of UNION ALL is always
|
||||||
|
* unsorted.
|
||||||
|
*/
|
||||||
|
if (parse->sortClause)
|
||||||
|
tuple_fraction = 0.0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Construct the plan for set operations. The result will not
|
* Construct the plan for set operations. The result will not
|
||||||
* need any work except perhaps a top-level sort and/or LIMIT.
|
* need any work except perhaps a top-level sort and/or LIMIT.
|
||||||
*/
|
*/
|
||||||
result_plan = plan_set_operations(root,
|
result_plan = plan_set_operations(root, tuple_fraction,
|
||||||
&set_sortclauses);
|
&set_sortclauses);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -769,108 +786,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
|
|||||||
else
|
else
|
||||||
root->query_pathkeys = NIL;
|
root->query_pathkeys = NIL;
|
||||||
|
|
||||||
/*
|
|
||||||
* Adjust tuple_fraction if we see that we are going to apply
|
|
||||||
* limiting/grouping/aggregation/etc. This is not overridable by
|
|
||||||
* the caller, since it reflects plan actions that this routine
|
|
||||||
* will certainly take, not assumptions about context.
|
|
||||||
*/
|
|
||||||
if (parse->limitCount != NULL)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* A LIMIT clause limits the absolute number of tuples
|
|
||||||
* returned. However, if it's not a constant LIMIT then we
|
|
||||||
* have to punt; for lack of a better idea, assume 10% of the
|
|
||||||
* plan's result is wanted.
|
|
||||||
*/
|
|
||||||
double limit_fraction = 0.0;
|
|
||||||
|
|
||||||
if (IsA(parse->limitCount, Const))
|
|
||||||
{
|
|
||||||
Const *limitc = (Const *) parse->limitCount;
|
|
||||||
int32 count = DatumGetInt32(limitc->constvalue);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A NULL-constant LIMIT represents "LIMIT ALL", which we
|
|
||||||
* treat the same as no limit (ie, expect to retrieve all
|
|
||||||
* the tuples).
|
|
||||||
*/
|
|
||||||
if (!limitc->constisnull && count > 0)
|
|
||||||
{
|
|
||||||
limit_fraction = (double) count;
|
|
||||||
/* We must also consider the OFFSET, if present */
|
|
||||||
if (parse->limitOffset != NULL)
|
|
||||||
{
|
|
||||||
if (IsA(parse->limitOffset, Const))
|
|
||||||
{
|
|
||||||
int32 offset;
|
|
||||||
|
|
||||||
limitc = (Const *) parse->limitOffset;
|
|
||||||
offset = DatumGetInt32(limitc->constvalue);
|
|
||||||
if (!limitc->constisnull && offset > 0)
|
|
||||||
limit_fraction += (double) offset;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* OFFSET is an expression ... punt ... */
|
|
||||||
limit_fraction = 0.10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* LIMIT is an expression ... punt ... */
|
|
||||||
limit_fraction = 0.10;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (limit_fraction > 0.0)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* If we have absolute limits from both caller and LIMIT,
|
|
||||||
* use the smaller value; if one is fractional and the
|
|
||||||
* other absolute, treat the fraction as a fraction of the
|
|
||||||
* absolute value; else we can multiply the two fractions
|
|
||||||
* together.
|
|
||||||
*/
|
|
||||||
if (tuple_fraction >= 1.0)
|
|
||||||
{
|
|
||||||
if (limit_fraction >= 1.0)
|
|
||||||
{
|
|
||||||
/* both absolute */
|
|
||||||
tuple_fraction = Min(tuple_fraction, limit_fraction);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* caller absolute, limit fractional */
|
|
||||||
tuple_fraction *= limit_fraction;
|
|
||||||
if (tuple_fraction < 1.0)
|
|
||||||
tuple_fraction = 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (tuple_fraction > 0.0)
|
|
||||||
{
|
|
||||||
if (limit_fraction >= 1.0)
|
|
||||||
{
|
|
||||||
/* caller fractional, limit absolute */
|
|
||||||
tuple_fraction *= limit_fraction;
|
|
||||||
if (tuple_fraction < 1.0)
|
|
||||||
tuple_fraction = 1.0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* both fractional */
|
|
||||||
tuple_fraction *= limit_fraction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* no info from caller, just use limit */
|
|
||||||
tuple_fraction = limit_fraction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* With grouping or aggregation, the tuple fraction to pass to
|
* With grouping or aggregation, the tuple fraction to pass to
|
||||||
* query_planner() may be different from what it is at top level.
|
* query_planner() may be different from what it is at top level.
|
||||||
@ -1242,6 +1157,114 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
|
|||||||
return result_plan;
|
return result_plan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* adjust_tuple_fraction_for_limit - adjust tuple fraction for LIMIT
|
||||||
|
*
|
||||||
|
* If the query contains LIMIT, we adjust the caller-supplied tuple_fraction
|
||||||
|
* accordingly. This is not overridable by the caller, since it reflects plan
|
||||||
|
* actions that grouping_planner() will certainly take, not assumptions about
|
||||||
|
* context.
|
||||||
|
*/
|
||||||
|
static double
|
||||||
|
adjust_tuple_fraction_for_limit(PlannerInfo *root, double tuple_fraction)
|
||||||
|
{
|
||||||
|
Query *parse = root->parse;
|
||||||
|
double limit_fraction = 0.0;
|
||||||
|
|
||||||
|
/* Should not be called unless LIMIT */
|
||||||
|
Assert(parse->limitCount != NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A LIMIT clause limits the absolute number of tuples returned. However,
|
||||||
|
* if it's not a constant LIMIT then we have to punt; for lack of a better
|
||||||
|
* idea, assume 10% of the plan's result is wanted.
|
||||||
|
*/
|
||||||
|
if (IsA(parse->limitCount, Const))
|
||||||
|
{
|
||||||
|
Const *limitc = (Const *) parse->limitCount;
|
||||||
|
int32 count = DatumGetInt32(limitc->constvalue);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A NULL-constant LIMIT represents "LIMIT ALL", which we treat the
|
||||||
|
* same as no limit (ie, expect to retrieve all the tuples).
|
||||||
|
*/
|
||||||
|
if (!limitc->constisnull && count > 0)
|
||||||
|
{
|
||||||
|
limit_fraction = (double) count;
|
||||||
|
/* We must also consider the OFFSET, if present */
|
||||||
|
if (parse->limitOffset != NULL)
|
||||||
|
{
|
||||||
|
if (IsA(parse->limitOffset, Const))
|
||||||
|
{
|
||||||
|
int32 offset;
|
||||||
|
|
||||||
|
limitc = (Const *) parse->limitOffset;
|
||||||
|
offset = DatumGetInt32(limitc->constvalue);
|
||||||
|
if (!limitc->constisnull && offset > 0)
|
||||||
|
limit_fraction += (double) offset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* OFFSET is an expression ... punt ... */
|
||||||
|
limit_fraction = 0.10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* LIMIT is an expression ... punt ... */
|
||||||
|
limit_fraction = 0.10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (limit_fraction > 0.0)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If we have absolute limits from both caller and LIMIT, use the
|
||||||
|
* smaller value; if one is fractional and the other absolute,
|
||||||
|
* treat the fraction as a fraction of the absolute value;
|
||||||
|
* else we can multiply the two fractions together.
|
||||||
|
*/
|
||||||
|
if (tuple_fraction >= 1.0)
|
||||||
|
{
|
||||||
|
if (limit_fraction >= 1.0)
|
||||||
|
{
|
||||||
|
/* both absolute */
|
||||||
|
tuple_fraction = Min(tuple_fraction, limit_fraction);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* caller absolute, limit fractional */
|
||||||
|
tuple_fraction *= limit_fraction;
|
||||||
|
if (tuple_fraction < 1.0)
|
||||||
|
tuple_fraction = 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (tuple_fraction > 0.0)
|
||||||
|
{
|
||||||
|
if (limit_fraction >= 1.0)
|
||||||
|
{
|
||||||
|
/* caller fractional, limit absolute */
|
||||||
|
tuple_fraction *= limit_fraction;
|
||||||
|
if (tuple_fraction < 1.0)
|
||||||
|
tuple_fraction = 1.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* both fractional */
|
||||||
|
tuple_fraction *= limit_fraction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* no info from caller, just use limit */
|
||||||
|
tuple_fraction = limit_fraction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tuple_fraction;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* choose_hashed_grouping - should we use hashed grouping?
|
* choose_hashed_grouping - should we use hashed grouping?
|
||||||
*/
|
*/
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.123 2005/06/09 04:18:59 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.124 2005/06/10 02:21:05 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -50,14 +50,17 @@ typedef struct
|
|||||||
} adjust_inherited_attrs_context;
|
} adjust_inherited_attrs_context;
|
||||||
|
|
||||||
static Plan *recurse_set_operations(Node *setOp, PlannerInfo *root,
|
static Plan *recurse_set_operations(Node *setOp, PlannerInfo *root,
|
||||||
|
double tuple_fraction,
|
||||||
List *colTypes, bool junkOK,
|
List *colTypes, bool junkOK,
|
||||||
int flag, List *refnames_tlist,
|
int flag, List *refnames_tlist,
|
||||||
List **sortClauses);
|
List **sortClauses);
|
||||||
static Plan *generate_union_plan(SetOperationStmt *op, PlannerInfo *root,
|
static Plan *generate_union_plan(SetOperationStmt *op, PlannerInfo *root,
|
||||||
|
double tuple_fraction,
|
||||||
List *refnames_tlist, List **sortClauses);
|
List *refnames_tlist, List **sortClauses);
|
||||||
static Plan *generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root,
|
static Plan *generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root,
|
||||||
List *refnames_tlist, List **sortClauses);
|
List *refnames_tlist, List **sortClauses);
|
||||||
static List *recurse_union_children(Node *setOp, PlannerInfo *root,
|
static List *recurse_union_children(Node *setOp, PlannerInfo *root,
|
||||||
|
double tuple_fraction,
|
||||||
SetOperationStmt *top_union,
|
SetOperationStmt *top_union,
|
||||||
List *refnames_tlist);
|
List *refnames_tlist);
|
||||||
static List *generate_setop_tlist(List *colTypes, int flag,
|
static List *generate_setop_tlist(List *colTypes, int flag,
|
||||||
@ -85,11 +88,17 @@ static List *adjust_inherited_tlist(List *tlist,
|
|||||||
* Any top-level ORDER BY requested in root->parse->sortClause will be added
|
* Any top-level ORDER BY requested in root->parse->sortClause will be added
|
||||||
* when we return to grouping_planner.
|
* when we return to grouping_planner.
|
||||||
*
|
*
|
||||||
|
* tuple_fraction is the fraction of tuples we expect will be retrieved.
|
||||||
|
* tuple_fraction is interpreted as for grouping_planner(); in particular,
|
||||||
|
* zero means "all the tuples will be fetched". Any LIMIT present at the
|
||||||
|
* top level has already been factored into tuple_fraction.
|
||||||
|
*
|
||||||
* *sortClauses is an output argument: it is set to a list of SortClauses
|
* *sortClauses is an output argument: it is set to a list of SortClauses
|
||||||
* representing the result ordering of the topmost set operation.
|
* representing the result ordering of the topmost set operation.
|
||||||
*/
|
*/
|
||||||
Plan *
|
Plan *
|
||||||
plan_set_operations(PlannerInfo *root, List **sortClauses)
|
plan_set_operations(PlannerInfo *root, double tuple_fraction,
|
||||||
|
List **sortClauses)
|
||||||
{
|
{
|
||||||
Query *parse = root->parse;
|
Query *parse = root->parse;
|
||||||
SetOperationStmt *topop = (SetOperationStmt *) parse->setOperations;
|
SetOperationStmt *topop = (SetOperationStmt *) parse->setOperations;
|
||||||
@ -124,7 +133,7 @@ plan_set_operations(PlannerInfo *root, List **sortClauses)
|
|||||||
* output from the top-level node, plus possibly resjunk working
|
* output from the top-level node, plus possibly resjunk working
|
||||||
* columns (we can rely on upper-level nodes to deal with that).
|
* columns (we can rely on upper-level nodes to deal with that).
|
||||||
*/
|
*/
|
||||||
return recurse_set_operations((Node *) topop, root,
|
return recurse_set_operations((Node *) topop, root, tuple_fraction,
|
||||||
topop->colTypes, true, -1,
|
topop->colTypes, true, -1,
|
||||||
leftmostQuery->targetList,
|
leftmostQuery->targetList,
|
||||||
sortClauses);
|
sortClauses);
|
||||||
@ -134,6 +143,7 @@ plan_set_operations(PlannerInfo *root, List **sortClauses)
|
|||||||
* recurse_set_operations
|
* recurse_set_operations
|
||||||
* Recursively handle one step in a tree of set operations
|
* Recursively handle one step in a tree of set operations
|
||||||
*
|
*
|
||||||
|
* tuple_fraction: fraction of tuples we expect to retrieve from node
|
||||||
* colTypes: list of type OIDs of expected output columns
|
* colTypes: list of type OIDs of expected output columns
|
||||||
* junkOK: if true, child resjunk columns may be left in the result
|
* junkOK: if true, child resjunk columns may be left in the result
|
||||||
* flag: if >= 0, add a resjunk output column indicating value of flag
|
* flag: if >= 0, add a resjunk output column indicating value of flag
|
||||||
@ -142,6 +152,7 @@ plan_set_operations(PlannerInfo *root, List **sortClauses)
|
|||||||
*/
|
*/
|
||||||
static Plan *
|
static Plan *
|
||||||
recurse_set_operations(Node *setOp, PlannerInfo *root,
|
recurse_set_operations(Node *setOp, PlannerInfo *root,
|
||||||
|
double tuple_fraction,
|
||||||
List *colTypes, bool junkOK,
|
List *colTypes, bool junkOK,
|
||||||
int flag, List *refnames_tlist,
|
int flag, List *refnames_tlist,
|
||||||
List **sortClauses)
|
List **sortClauses)
|
||||||
@ -159,7 +170,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
|
|||||||
/*
|
/*
|
||||||
* Generate plan for primitive subquery
|
* Generate plan for primitive subquery
|
||||||
*/
|
*/
|
||||||
subplan = subquery_planner(subquery, 0.0 /* default case */, NULL);
|
subplan = subquery_planner(subquery, tuple_fraction, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a SubqueryScan with the caller-requested targetlist
|
* Add a SubqueryScan with the caller-requested targetlist
|
||||||
@ -189,10 +200,12 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
|
|||||||
|
|
||||||
/* UNIONs are much different from INTERSECT/EXCEPT */
|
/* UNIONs are much different from INTERSECT/EXCEPT */
|
||||||
if (op->op == SETOP_UNION)
|
if (op->op == SETOP_UNION)
|
||||||
plan = generate_union_plan(op, root, refnames_tlist,
|
plan = generate_union_plan(op, root, tuple_fraction,
|
||||||
|
refnames_tlist,
|
||||||
sortClauses);
|
sortClauses);
|
||||||
else
|
else
|
||||||
plan = generate_nonunion_plan(op, root, refnames_tlist,
|
plan = generate_nonunion_plan(op, root,
|
||||||
|
refnames_tlist,
|
||||||
sortClauses);
|
sortClauses);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -235,6 +248,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
|
|||||||
*/
|
*/
|
||||||
static Plan *
|
static Plan *
|
||||||
generate_union_plan(SetOperationStmt *op, PlannerInfo *root,
|
generate_union_plan(SetOperationStmt *op, PlannerInfo *root,
|
||||||
|
double tuple_fraction,
|
||||||
List *refnames_tlist,
|
List *refnames_tlist,
|
||||||
List **sortClauses)
|
List **sortClauses)
|
||||||
{
|
{
|
||||||
@ -242,6 +256,20 @@ generate_union_plan(SetOperationStmt *op, PlannerInfo *root,
|
|||||||
List *tlist;
|
List *tlist;
|
||||||
Plan *plan;
|
Plan *plan;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If plain UNION, tell children to fetch all tuples.
|
||||||
|
*
|
||||||
|
* Note: in UNION ALL, we pass the top-level tuple_fraction unmodified
|
||||||
|
* to each arm of the UNION ALL. One could make a case for reducing
|
||||||
|
* the tuple fraction for later arms (discounting by the expected size
|
||||||
|
* of the earlier arms' results) but it seems not worth the trouble.
|
||||||
|
* The normal case where tuple_fraction isn't already zero is a LIMIT
|
||||||
|
* at top level, and passing it down as-is is usually enough to get the
|
||||||
|
* desired result of preferring fast-start plans.
|
||||||
|
*/
|
||||||
|
if (!op->all)
|
||||||
|
tuple_fraction = 0.0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If any of my children are identical UNION nodes (same op, all-flag,
|
* If any of my children are identical UNION nodes (same op, all-flag,
|
||||||
* and colTypes) then they can be merged into this node so that we
|
* and colTypes) then they can be merged into this node so that we
|
||||||
@ -249,8 +277,10 @@ generate_union_plan(SetOperationStmt *op, PlannerInfo *root,
|
|||||||
* such nodes and compute their children's plans.
|
* such nodes and compute their children's plans.
|
||||||
*/
|
*/
|
||||||
planlist = list_concat(recurse_union_children(op->larg, root,
|
planlist = list_concat(recurse_union_children(op->larg, root,
|
||||||
|
tuple_fraction,
|
||||||
op, refnames_tlist),
|
op, refnames_tlist),
|
||||||
recurse_union_children(op->rarg, root,
|
recurse_union_children(op->rarg, root,
|
||||||
|
tuple_fraction,
|
||||||
op, refnames_tlist));
|
op, refnames_tlist));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -309,10 +339,12 @@ generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root,
|
|||||||
|
|
||||||
/* Recurse on children, ensuring their outputs are marked */
|
/* Recurse on children, ensuring their outputs are marked */
|
||||||
lplan = recurse_set_operations(op->larg, root,
|
lplan = recurse_set_operations(op->larg, root,
|
||||||
|
0.0 /* all tuples needed */,
|
||||||
op->colTypes, false, 0,
|
op->colTypes, false, 0,
|
||||||
refnames_tlist,
|
refnames_tlist,
|
||||||
&child_sortclauses);
|
&child_sortclauses);
|
||||||
rplan = recurse_set_operations(op->rarg, root,
|
rplan = recurse_set_operations(op->rarg, root,
|
||||||
|
0.0 /* all tuples needed */,
|
||||||
op->colTypes, false, 1,
|
op->colTypes, false, 1,
|
||||||
refnames_tlist,
|
refnames_tlist,
|
||||||
&child_sortclauses);
|
&child_sortclauses);
|
||||||
@ -377,6 +409,7 @@ generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root,
|
|||||||
*/
|
*/
|
||||||
static List *
|
static List *
|
||||||
recurse_union_children(Node *setOp, PlannerInfo *root,
|
recurse_union_children(Node *setOp, PlannerInfo *root,
|
||||||
|
double tuple_fraction,
|
||||||
SetOperationStmt *top_union,
|
SetOperationStmt *top_union,
|
||||||
List *refnames_tlist)
|
List *refnames_tlist)
|
||||||
{
|
{
|
||||||
@ -392,9 +425,11 @@ recurse_union_children(Node *setOp, PlannerInfo *root,
|
|||||||
{
|
{
|
||||||
/* Same UNION, so fold children into parent's subplan list */
|
/* Same UNION, so fold children into parent's subplan list */
|
||||||
return list_concat(recurse_union_children(op->larg, root,
|
return list_concat(recurse_union_children(op->larg, root,
|
||||||
|
tuple_fraction,
|
||||||
top_union,
|
top_union,
|
||||||
refnames_tlist),
|
refnames_tlist),
|
||||||
recurse_union_children(op->rarg, root,
|
recurse_union_children(op->rarg, root,
|
||||||
|
tuple_fraction,
|
||||||
top_union,
|
top_union,
|
||||||
refnames_tlist));
|
refnames_tlist));
|
||||||
}
|
}
|
||||||
@ -411,6 +446,7 @@ recurse_union_children(Node *setOp, PlannerInfo *root,
|
|||||||
* resjunk anyway.
|
* resjunk anyway.
|
||||||
*/
|
*/
|
||||||
return list_make1(recurse_set_operations(setOp, root,
|
return list_make1(recurse_set_operations(setOp, root,
|
||||||
|
tuple_fraction,
|
||||||
top_union->colTypes, false,
|
top_union->colTypes, false,
|
||||||
-1, refnames_tlist,
|
-1, refnames_tlist,
|
||||||
&child_sortclauses));
|
&child_sortclauses));
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.50 2005/06/05 22:32:58 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.51 2005/06/10 02:21:05 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -45,7 +45,8 @@ extern List *preprocess_targetlist(PlannerInfo *root, List *tlist);
|
|||||||
/*
|
/*
|
||||||
* prototypes for prepunion.c
|
* prototypes for prepunion.c
|
||||||
*/
|
*/
|
||||||
extern Plan *plan_set_operations(PlannerInfo *root, List **sortClauses);
|
extern Plan *plan_set_operations(PlannerInfo *root, double tuple_fraction,
|
||||||
|
List **sortClauses);
|
||||||
|
|
||||||
extern List *find_all_inheritors(Oid parentrel);
|
extern List *find_all_inheritors(Oid parentrel);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user