Skip to content

Commit 3b167a4

Browse files
committed
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.
1 parent 39cee73 commit 3b167a4

File tree

3 files changed

+178
-118
lines changed

3 files changed

+178
-118
lines changed

src/backend/optimizer/plan/planner.c

Lines changed: 127 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.188 2005/06/05 22:32:56 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.189 2005/06/10 02:21:04 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -58,6 +58,8 @@ static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
5858
static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
5959
static Plan *inheritance_planner(PlannerInfo *root, List *inheritlist);
6060
static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction);
61+
static double adjust_tuple_fraction_for_limit(PlannerInfo *root,
62+
double tuple_fraction);
6163
static bool choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
6264
Path *cheapest_path, Path *sorted_path,
6365
List *sort_pathkeys, List *group_pathkeys,
@@ -648,15 +650,30 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
648650
List *current_pathkeys;
649651
List *sort_pathkeys;
650652

653+
/* Tweak caller-supplied tuple_fraction if have LIMIT */
654+
if (parse->limitCount != NULL)
655+
tuple_fraction = adjust_tuple_fraction_for_limit(root, tuple_fraction);
656+
651657
if (parse->setOperations)
652658
{
653659
List *set_sortclauses;
654660

661+
/*
662+
* If there's a top-level ORDER BY, assume we have to fetch all
663+
* the tuples. This might seem too simplistic given all the
664+
* hackery below to possibly avoid the sort ... but a nonzero
665+
* tuple_fraction is only of use to plan_set_operations() when
666+
* the setop is UNION ALL, and the result of UNION ALL is always
667+
* unsorted.
668+
*/
669+
if (parse->sortClause)
670+
tuple_fraction = 0.0;
671+
655672
/*
656673
* Construct the plan for set operations. The result will not
657674
* need any work except perhaps a top-level sort and/or LIMIT.
658675
*/
659-
result_plan = plan_set_operations(root,
676+
result_plan = plan_set_operations(root, tuple_fraction,
660677
&set_sortclauses);
661678

662679
/*
@@ -769,108 +786,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
769786
else
770787
root->query_pathkeys = NIL;
771788

772-
/*
773-
* Adjust tuple_fraction if we see that we are going to apply
774-
* limiting/grouping/aggregation/etc. This is not overridable by
775-
* the caller, since it reflects plan actions that this routine
776-
* will certainly take, not assumptions about context.
777-
*/
778-
if (parse->limitCount != NULL)
779-
{
780-
/*
781-
* A LIMIT clause limits the absolute number of tuples
782-
* returned. However, if it's not a constant LIMIT then we
783-
* have to punt; for lack of a better idea, assume 10% of the
784-
* plan's result is wanted.
785-
*/
786-
double limit_fraction = 0.0;
787-
788-
if (IsA(parse->limitCount, Const))
789-
{
790-
Const *limitc = (Const *) parse->limitCount;
791-
int32 count = DatumGetInt32(limitc->constvalue);
792-
793-
/*
794-
* A NULL-constant LIMIT represents "LIMIT ALL", which we
795-
* treat the same as no limit (ie, expect to retrieve all
796-
* the tuples).
797-
*/
798-
if (!limitc->constisnull && count > 0)
799-
{
800-
limit_fraction = (double) count;
801-
/* We must also consider the OFFSET, if present */
802-
if (parse->limitOffset != NULL)
803-
{
804-
if (IsA(parse->limitOffset, Const))
805-
{
806-
int32 offset;
807-
808-
limitc = (Const *) parse->limitOffset;
809-
offset = DatumGetInt32(limitc->constvalue);
810-
if (!limitc->constisnull && offset > 0)
811-
limit_fraction += (double) offset;
812-
}
813-
else
814-
{
815-
/* OFFSET is an expression ... punt ... */
816-
limit_fraction = 0.10;
817-
}
818-
}
819-
}
820-
}
821-
else
822-
{
823-
/* LIMIT is an expression ... punt ... */
824-
limit_fraction = 0.10;
825-
}
826-
827-
if (limit_fraction > 0.0)
828-
{
829-
/*
830-
* If we have absolute limits from both caller and LIMIT,
831-
* use the smaller value; if one is fractional and the
832-
* other absolute, treat the fraction as a fraction of the
833-
* absolute value; else we can multiply the two fractions
834-
* together.
835-
*/
836-
if (tuple_fraction >= 1.0)
837-
{
838-
if (limit_fraction >= 1.0)
839-
{
840-
/* both absolute */
841-
tuple_fraction = Min(tuple_fraction, limit_fraction);
842-
}
843-
else
844-
{
845-
/* caller absolute, limit fractional */
846-
tuple_fraction *= limit_fraction;
847-
if (tuple_fraction < 1.0)
848-
tuple_fraction = 1.0;
849-
}
850-
}
851-
else if (tuple_fraction > 0.0)
852-
{
853-
if (limit_fraction >= 1.0)
854-
{
855-
/* caller fractional, limit absolute */
856-
tuple_fraction *= limit_fraction;
857-
if (tuple_fraction < 1.0)
858-
tuple_fraction = 1.0;
859-
}
860-
else
861-
{
862-
/* both fractional */
863-
tuple_fraction *= limit_fraction;
864-
}
865-
}
866-
else
867-
{
868-
/* no info from caller, just use limit */
869-
tuple_fraction = limit_fraction;
870-
}
871-
}
872-
}
873-
874789
/*
875790
* With grouping or aggregation, the tuple fraction to pass to
876791
* query_planner() may be different from what it is at top level.
@@ -1242,6 +1157,114 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
12421157
return result_plan;
12431158
}
12441159

1160+
/*
1161+
* adjust_tuple_fraction_for_limit - adjust tuple fraction for LIMIT
1162+
*
1163+
* If the query contains LIMIT, we adjust the caller-supplied tuple_fraction
1164+
* accordingly. This is not overridable by the caller, since it reflects plan
1165+
* actions that grouping_planner() will certainly take, not assumptions about
1166+
* context.
1167+
*/
1168+
static double
1169+
adjust_tuple_fraction_for_limit(PlannerInfo *root, double tuple_fraction)
1170+
{
1171+
Query *parse = root->parse;
1172+
double limit_fraction = 0.0;
1173+
1174+
/* Should not be called unless LIMIT */
1175+
Assert(parse->limitCount != NULL);
1176+
1177+
/*
1178+
* A LIMIT clause limits the absolute number of tuples returned. However,
1179+
* if it's not a constant LIMIT then we have to punt; for lack of a better
1180+
* idea, assume 10% of the plan's result is wanted.
1181+
*/
1182+
if (IsA(parse->limitCount, Const))
1183+
{
1184+
Const *limitc = (Const *) parse->limitCount;
1185+
int32 count = DatumGetInt32(limitc->constvalue);
1186+
1187+
/*
1188+
* A NULL-constant LIMIT represents "LIMIT ALL", which we treat the
1189+
* same as no limit (ie, expect to retrieve all the tuples).
1190+
*/
1191+
if (!limitc->constisnull && count > 0)
1192+
{
1193+
limit_fraction = (double) count;
1194+
/* We must also consider the OFFSET, if present */
1195+
if (parse->limitOffset != NULL)
1196+
{
1197+
if (IsA(parse->limitOffset, Const))
1198+
{
1199+
int32 offset;
1200+
1201+
limitc = (Const *) parse->limitOffset;
1202+
offset = DatumGetInt32(limitc->constvalue);
1203+
if (!limitc->constisnull && offset > 0)
1204+
limit_fraction += (double) offset;
1205+
}
1206+
else
1207+
{
1208+
/* OFFSET is an expression ... punt ... */
1209+
limit_fraction = 0.10;
1210+
}
1211+
}
1212+
}
1213+
}
1214+
else
1215+
{
1216+
/* LIMIT is an expression ... punt ... */
1217+
limit_fraction = 0.10;
1218+
}
1219+
1220+
if (limit_fraction > 0.0)
1221+
{
1222+
/*
1223+
* If we have absolute limits from both caller and LIMIT, use the
1224+
* smaller value; if one is fractional and the other absolute,
1225+
* treat the fraction as a fraction of the absolute value;
1226+
* else we can multiply the two fractions together.
1227+
*/
1228+
if (tuple_fraction >= 1.0)
1229+
{
1230+
if (limit_fraction >= 1.0)
1231+
{
1232+
/* both absolute */
1233+
tuple_fraction = Min(tuple_fraction, limit_fraction);
1234+
}
1235+
else
1236+
{
1237+
/* caller absolute, limit fractional */
1238+
tuple_fraction *= limit_fraction;
1239+
if (tuple_fraction < 1.0)
1240+
tuple_fraction = 1.0;
1241+
}
1242+
}
1243+
else if (tuple_fraction > 0.0)
1244+
{
1245+
if (limit_fraction >= 1.0)
1246+
{
1247+
/* caller fractional, limit absolute */
1248+
tuple_fraction *= limit_fraction;
1249+
if (tuple_fraction < 1.0)
1250+
tuple_fraction = 1.0;
1251+
}
1252+
else
1253+
{
1254+
/* both fractional */
1255+
tuple_fraction *= limit_fraction;
1256+
}
1257+
}
1258+
else
1259+
{
1260+
/* no info from caller, just use limit */
1261+
tuple_fraction = limit_fraction;
1262+
}
1263+
}
1264+
1265+
return tuple_fraction;
1266+
}
1267+
12451268
/*
12461269
* choose_hashed_grouping - should we use hashed grouping?
12471270
*/

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy