]> granicus.if.org Git - postgresql/commitdiff
Avoid inserting no-op Limit plan nodes.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 14 Mar 2013 19:10:41 +0000 (15:10 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 14 Mar 2013 19:11:05 +0000 (15:11 -0400)
This was discussed in connection with the patch to avoid inserting no-op
Result nodes, but not actually implemented therein.

src/backend/optimizer/plan/planner.c
src/test/regress/expected/updatable_views.out

index 670776b032b40c75d38e63c4795c6ee343024097..db91b8277d9966d3597996a1f574233e6b062241 100644 (file)
@@ -68,6 +68,7 @@ static void preprocess_rowmarks(PlannerInfo *root);
 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,
@@ -1825,7 +1826,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
        /*
         * 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,
@@ -2296,6 +2297,60 @@ preprocess_limit(PlannerInfo *root, double 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
index ead08d69b10fd302acbc0d8e5438ea1aad22ccff..005bb0961e8b7c10464b6d61bdbc9339c1438379 100644 (file)
@@ -542,36 +542,34 @@ SELECT * FROM rw_view2;
 (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
@@ -775,30 +773,28 @@ SELECT * FROM rw_view2;
 (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