mirror of
https://github.com/postgres/postgres.git
synced 2025-06-13 07:41:39 +03:00
Avoid inserting no-op Limit plan nodes.
This was discussed in connection with the patch to avoid inserting no-op Result nodes, but not actually implemented therein.
This commit is contained in:
@ -68,6 +68,7 @@ static void preprocess_rowmarks(PlannerInfo *root);
|
|||||||
static double preprocess_limit(PlannerInfo *root,
|
static double preprocess_limit(PlannerInfo *root,
|
||||||
double tuple_fraction,
|
double tuple_fraction,
|
||||||
int64 *offset_est, int64 *count_est);
|
int64 *offset_est, int64 *count_est);
|
||||||
|
static bool limit_needed(Query *parse);
|
||||||
static void preprocess_groupclause(PlannerInfo *root);
|
static void preprocess_groupclause(PlannerInfo *root);
|
||||||
static bool choose_hashed_grouping(PlannerInfo *root,
|
static bool choose_hashed_grouping(PlannerInfo *root,
|
||||||
double tuple_fraction, double limit_tuples,
|
double tuple_fraction, double limit_tuples,
|
||||||
@ -1825,7 +1826,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
|
|||||||
/*
|
/*
|
||||||
* Finally, if there is a LIMIT/OFFSET clause, add the LIMIT node.
|
* Finally, if there is a LIMIT/OFFSET clause, add the LIMIT node.
|
||||||
*/
|
*/
|
||||||
if (parse->limitCount || parse->limitOffset)
|
if (limit_needed(parse))
|
||||||
{
|
{
|
||||||
result_plan = (Plan *) make_limit(result_plan,
|
result_plan = (Plan *) make_limit(result_plan,
|
||||||
parse->limitOffset,
|
parse->limitOffset,
|
||||||
@ -2296,6 +2297,60 @@ preprocess_limit(PlannerInfo *root, double tuple_fraction,
|
|||||||
return tuple_fraction;
|
return tuple_fraction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* limit_needed - do we actually need a Limit plan node?
|
||||||
|
*
|
||||||
|
* If we have constant-zero OFFSET and constant-null LIMIT, we can skip adding
|
||||||
|
* a Limit node. This is worth checking for because "OFFSET 0" is a common
|
||||||
|
* locution for an optimization fence. (Because other places in the planner
|
||||||
|
* merely check whether parse->limitOffset isn't NULL, it will still work as
|
||||||
|
* an optimization fence --- we're just suppressing unnecessary run-time
|
||||||
|
* overhead.)
|
||||||
|
*
|
||||||
|
* This might look like it could be merged into preprocess_limit, but there's
|
||||||
|
* a key distinction: here we need hard constants in OFFSET/LIMIT, whereas
|
||||||
|
* in preprocess_limit it's good enough to consider estimated values.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
limit_needed(Query *parse)
|
||||||
|
{
|
||||||
|
Node *node;
|
||||||
|
|
||||||
|
node = parse->limitCount;
|
||||||
|
if (node)
|
||||||
|
{
|
||||||
|
if (IsA(node, Const))
|
||||||
|
{
|
||||||
|
/* NULL indicates LIMIT ALL, ie, no limit */
|
||||||
|
if (!((Const *) node)->constisnull)
|
||||||
|
return true; /* LIMIT with a constant value */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return true; /* non-constant LIMIT */
|
||||||
|
}
|
||||||
|
|
||||||
|
node = parse->limitOffset;
|
||||||
|
if (node)
|
||||||
|
{
|
||||||
|
if (IsA(node, Const))
|
||||||
|
{
|
||||||
|
/* Treat NULL as no offset; the executor would too */
|
||||||
|
if (!((Const *) node)->constisnull)
|
||||||
|
{
|
||||||
|
int64 offset = DatumGetInt64(((Const *) node)->constvalue);
|
||||||
|
|
||||||
|
/* Executor would treat less-than-zero same as zero */
|
||||||
|
if (offset > 0)
|
||||||
|
return true; /* OFFSET with a positive value */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return true; /* non-constant OFFSET */
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; /* don't need a Limit plan node */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* preprocess_groupclause - do preparatory work on GROUP BY clause
|
* preprocess_groupclause - do preparatory work on GROUP BY clause
|
||||||
|
@ -542,36 +542,34 @@ SELECT * FROM rw_view2;
|
|||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
EXPLAIN (costs off) UPDATE rw_view2 SET a=3 WHERE a=2;
|
EXPLAIN (costs off) UPDATE rw_view2 SET a=3 WHERE a=2;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
Update on base_tbl
|
Update on base_tbl
|
||||||
-> Nested Loop
|
-> Nested Loop
|
||||||
-> Index Scan using base_tbl_pkey on base_tbl
|
-> Index Scan using base_tbl_pkey on base_tbl
|
||||||
Index Cond: (a = 2)
|
Index Cond: (a = 2)
|
||||||
-> Subquery Scan on rw_view1
|
-> Subquery Scan on rw_view1
|
||||||
Filter: ((rw_view1.a < 10) AND (rw_view1.a = 2))
|
Filter: ((rw_view1.a < 10) AND (rw_view1.a = 2))
|
||||||
-> Limit
|
-> Bitmap Heap Scan on base_tbl base_tbl_1
|
||||||
-> Bitmap Heap Scan on base_tbl base_tbl_1
|
Recheck Cond: (a > 0)
|
||||||
Recheck Cond: (a > 0)
|
-> Bitmap Index Scan on base_tbl_pkey
|
||||||
-> Bitmap Index Scan on base_tbl_pkey
|
Index Cond: (a > 0)
|
||||||
Index Cond: (a > 0)
|
(10 rows)
|
||||||
(11 rows)
|
|
||||||
|
|
||||||
EXPLAIN (costs off) DELETE FROM rw_view2 WHERE a=2;
|
EXPLAIN (costs off) DELETE FROM rw_view2 WHERE a=2;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
Delete on base_tbl
|
Delete on base_tbl
|
||||||
-> Nested Loop
|
-> Nested Loop
|
||||||
-> Index Scan using base_tbl_pkey on base_tbl
|
-> Index Scan using base_tbl_pkey on base_tbl
|
||||||
Index Cond: (a = 2)
|
Index Cond: (a = 2)
|
||||||
-> Subquery Scan on rw_view1
|
-> Subquery Scan on rw_view1
|
||||||
Filter: ((rw_view1.a < 10) AND (rw_view1.a = 2))
|
Filter: ((rw_view1.a < 10) AND (rw_view1.a = 2))
|
||||||
-> Limit
|
-> Bitmap Heap Scan on base_tbl base_tbl_1
|
||||||
-> Bitmap Heap Scan on base_tbl base_tbl_1
|
Recheck Cond: (a > 0)
|
||||||
Recheck Cond: (a > 0)
|
-> Bitmap Index Scan on base_tbl_pkey
|
||||||
-> Bitmap Index Scan on base_tbl_pkey
|
Index Cond: (a > 0)
|
||||||
Index Cond: (a > 0)
|
(10 rows)
|
||||||
(11 rows)
|
|
||||||
|
|
||||||
DROP TABLE base_tbl CASCADE;
|
DROP TABLE base_tbl CASCADE;
|
||||||
NOTICE: drop cascades to 2 other objects
|
NOTICE: drop cascades to 2 other objects
|
||||||
@ -775,30 +773,28 @@ SELECT * FROM rw_view2;
|
|||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
EXPLAIN (costs off) UPDATE rw_view2 SET a=3 WHERE a=2;
|
EXPLAIN (costs off) UPDATE rw_view2 SET a=3 WHERE a=2;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------------
|
----------------------------------------------------------
|
||||||
Update on rw_view1 rw_view1_1
|
Update on rw_view1 rw_view1_1
|
||||||
-> Subquery Scan on rw_view1
|
-> Subquery Scan on rw_view1
|
||||||
Filter: ((rw_view1.a < 10) AND (rw_view1.a = 2))
|
Filter: ((rw_view1.a < 10) AND (rw_view1.a = 2))
|
||||||
-> Limit
|
-> Bitmap Heap Scan on base_tbl
|
||||||
-> Bitmap Heap Scan on base_tbl
|
Recheck Cond: (a > 0)
|
||||||
Recheck Cond: (a > 0)
|
-> Bitmap Index Scan on base_tbl_pkey
|
||||||
-> Bitmap Index Scan on base_tbl_pkey
|
Index Cond: (a > 0)
|
||||||
Index Cond: (a > 0)
|
(7 rows)
|
||||||
(8 rows)
|
|
||||||
|
|
||||||
EXPLAIN (costs off) DELETE FROM rw_view2 WHERE a=2;
|
EXPLAIN (costs off) DELETE FROM rw_view2 WHERE a=2;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------------
|
----------------------------------------------------------
|
||||||
Delete on rw_view1 rw_view1_1
|
Delete on rw_view1 rw_view1_1
|
||||||
-> Subquery Scan on rw_view1
|
-> Subquery Scan on rw_view1
|
||||||
Filter: ((rw_view1.a < 10) AND (rw_view1.a = 2))
|
Filter: ((rw_view1.a < 10) AND (rw_view1.a = 2))
|
||||||
-> Limit
|
-> Bitmap Heap Scan on base_tbl
|
||||||
-> Bitmap Heap Scan on base_tbl
|
Recheck Cond: (a > 0)
|
||||||
Recheck Cond: (a > 0)
|
-> Bitmap Index Scan on base_tbl_pkey
|
||||||
-> Bitmap Index Scan on base_tbl_pkey
|
Index Cond: (a > 0)
|
||||||
Index Cond: (a > 0)
|
(7 rows)
|
||||||
(8 rows)
|
|
||||||
|
|
||||||
DROP TABLE base_tbl CASCADE;
|
DROP TABLE base_tbl CASCADE;
|
||||||
NOTICE: drop cascades to 2 other objects
|
NOTICE: drop cascades to 2 other objects
|
||||||
|
Reference in New Issue
Block a user