static double preprocess_limit(PlannerInfo *root,
double tuple_fraction,
int64 *offset_est, int64 *count_est);
+static bool limit_needed(Query *parse);
static void preprocess_groupclause(PlannerInfo *root);
static bool choose_hashed_grouping(PlannerInfo *root,
double tuple_fraction, double limit_tuples,
/*
* 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,
parse->limitOffset,
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
(2 rows)
EXPLAIN (costs off) UPDATE rw_view2 SET a=3 WHERE a=2;
- QUERY PLAN
-------------------------------------------------------------------
+ QUERY PLAN
+----------------------------------------------------------------
Update on base_tbl
-> Nested Loop
-> Index Scan using base_tbl_pkey on base_tbl
Index Cond: (a = 2)
-> Subquery Scan on rw_view1
Filter: ((rw_view1.a < 10) AND (rw_view1.a = 2))
- -> Limit
- -> Bitmap Heap Scan on base_tbl base_tbl_1
- Recheck Cond: (a > 0)
- -> Bitmap Index Scan on base_tbl_pkey
- Index Cond: (a > 0)
-(11 rows)
+ -> Bitmap Heap Scan on base_tbl base_tbl_1
+ Recheck Cond: (a > 0)
+ -> Bitmap Index Scan on base_tbl_pkey
+ Index Cond: (a > 0)
+(10 rows)
EXPLAIN (costs off) DELETE FROM rw_view2 WHERE a=2;
- QUERY PLAN
-------------------------------------------------------------------
+ QUERY PLAN
+----------------------------------------------------------------
Delete on base_tbl
-> Nested Loop
-> Index Scan using base_tbl_pkey on base_tbl
Index Cond: (a = 2)
-> Subquery Scan on rw_view1
Filter: ((rw_view1.a < 10) AND (rw_view1.a = 2))
- -> Limit
- -> Bitmap Heap Scan on base_tbl base_tbl_1
- Recheck Cond: (a > 0)
- -> Bitmap Index Scan on base_tbl_pkey
- Index Cond: (a > 0)
-(11 rows)
+ -> Bitmap Heap Scan on base_tbl base_tbl_1
+ Recheck Cond: (a > 0)
+ -> Bitmap Index Scan on base_tbl_pkey
+ Index Cond: (a > 0)
+(10 rows)
DROP TABLE base_tbl CASCADE;
NOTICE: drop cascades to 2 other objects
(2 rows)
EXPLAIN (costs off) UPDATE rw_view2 SET a=3 WHERE a=2;
- QUERY PLAN
-------------------------------------------------------------
+ QUERY PLAN
+----------------------------------------------------------
Update on rw_view1 rw_view1_1
-> Subquery Scan on rw_view1
Filter: ((rw_view1.a < 10) AND (rw_view1.a = 2))
- -> Limit
- -> Bitmap Heap Scan on base_tbl
- Recheck Cond: (a > 0)
- -> Bitmap Index Scan on base_tbl_pkey
- Index Cond: (a > 0)
-(8 rows)
+ -> Bitmap Heap Scan on base_tbl
+ Recheck Cond: (a > 0)
+ -> Bitmap Index Scan on base_tbl_pkey
+ Index Cond: (a > 0)
+(7 rows)
EXPLAIN (costs off) DELETE FROM rw_view2 WHERE a=2;
- QUERY PLAN
-------------------------------------------------------------
+ QUERY PLAN
+----------------------------------------------------------
Delete on rw_view1 rw_view1_1
-> Subquery Scan on rw_view1
Filter: ((rw_view1.a < 10) AND (rw_view1.a = 2))
- -> Limit
- -> Bitmap Heap Scan on base_tbl
- Recheck Cond: (a > 0)
- -> Bitmap Index Scan on base_tbl_pkey
- Index Cond: (a > 0)
-(8 rows)
+ -> Bitmap Heap Scan on base_tbl
+ Recheck Cond: (a > 0)
+ -> Bitmap Index Scan on base_tbl_pkey
+ Index Cond: (a > 0)
+(7 rows)
DROP TABLE base_tbl CASCADE;
NOTICE: drop cascades to 2 other objects