From 2ef99ee708b0569c07535a67dc904aa8e24d9d29 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 1 Apr 2001 22:37:19 +0000 Subject: [PATCH] Planner wasn't correctly handling adjustment of tuple_fraction for the case of LIMIT in a sub-select. --- src/backend/optimizer/plan/planner.c | 160 +++++++++++++++++---------- 1 file changed, 102 insertions(+), 58 deletions(-) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index c5bd439587..199d27973b 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.102 2001/03/22 03:59:37 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.103 2001/04/01 22:37:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -874,9 +874,9 @@ grouping_planner(Query *parse, double tuple_fraction) /* * Figure out whether we expect to retrieve all the tuples that - * the plan can generate, or to stop early due to a LIMIT or other - * factors. If the caller passed a value >= 0, believe that - * value, else do our own examination of the query context. + * the plan can generate, or to stop early due to outside factors + * such as a cursor. If the caller passed a value >= 0, believe + * that value, else do our own examination of the query context. */ if (tuple_fraction < 0.0) { @@ -884,74 +884,118 @@ grouping_planner(Query *parse, double tuple_fraction) tuple_fraction = 0.0; /* - * Check for a LIMIT clause. + * Check for retrieve-into-portal, ie DECLARE CURSOR. + * + * We have no real idea how many tuples the user will ultimately + * FETCH from a cursor, but it seems a good bet that he + * doesn't want 'em all. Optimize for 10% retrieval (you + * gotta better number? Should this be a SETtable parameter?) + */ + if (parse->isPortal) + tuple_fraction = 0.10; + } + + /* + * 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. */ - if (parse->limitCount != NULL) + double limit_fraction = 0.0; + + if (IsA(parse->limitCount, Const)) { - 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) { - 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) { - tuple_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 { - if (IsA(parse->limitOffset, Const)) - { - int32 offset; - - limitc = (Const *) parse->limitOffset; - offset = DatumGetInt32(limitc->constvalue); - if (!limitc->constisnull && offset > 0) - tuple_fraction += (double) offset; - } - else - { - /* It's an expression ... punt ... */ - tuple_fraction = 0.10; - } + /* 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 { - - /* - * COUNT is an expression ... don't know exactly what - * the limit will be, but for lack of a better idea - * assume 10% of the plan's result is wanted. - */ - tuple_fraction = 0.10; + /* no info from caller, just use limit */ + tuple_fraction = limit_fraction; } } - - /* - * If no LIMIT, check for retrieve-into-portal, ie DECLARE - * CURSOR. - * - * We have no real idea how many tuples the user will ultimately - * FETCH from a cursor, but it seems a good bet that he - * doesn't want 'em all. Optimize for 10% retrieval (you - * gotta better number?) - */ - else if (parse->isPortal) - tuple_fraction = 0.10; } - /* - * Adjust tuple_fraction if we see that we are going to apply - * 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->groupClause) { -- GitLab