On 10th of June 2018, Tom Lane committed patch:

Improve run-time partition pruning to handle any stable expression.
 
 
The initial coding of the run-time-pruning feature only coped with cases
where the partition key(s) are compared to Params.  That is a bit silly;
we can allow it to work with any non-Var-containing stable expression, as
long as we take special care with expressions containing PARAM_EXEC Params.
The code is hardly any longer this way, and it's considerably clearer
(IMO at least).  Per gripe from Pavel Stehule.
 
David Rowley, whacked around a bit by me
 
Discussion: https://postgr.es/m/CAFj8pRBjrufA3ocDm8o4LPGNye9Y+pm1b9kCwode4X04CULG3g@mail.gmail.com

This is basically extension of work that I described earlier.

Previously, it looked that the pruning can happen only in some specific cases, which were rather tricky to explain.

Now, however, the pruning happens when you use any stable expression. For example – call to function that is marked stable or immutable.

Let's recreate our test case:

=$ create table users (
    id             serial not null,
    username       text not null,
    password       text
)
PARTITION BY RANGE ( id );
 
=$ CREATE TABLE users_0
    partition of users (id, primary key (id), unique (username))
    for values from (minvalue) to (10);
 
=$ CREATE TABLE users_1
    partition of users (id, primary key (id), unique (username))
    for values from (10) to (20);
=$ CREATE TABLE users_2
    partition of users (id, primary key (id), unique (username))
    for values from (20) to (30);
=$ CREATE TABLE users_3
    partition of users (id, primary key (id), unique (username))
    for values from (30) to (maxvalue);
 
=$ INSERT INTO users (username) SELECT 'u:' || i::text FROM generate_series(1,25) i;
 
=$ CREATE TABLE x (u_id INT4);
 
=$ INSERT INTO x (u_id) VALUES (13);
 
=$ ANALYZE;

Now, without this patch, if I'd try something like this:

=$ explain analyze select * from users where id = least(15,20);

I would get plan that checks all partitions:

                                                         QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
 Append  (cost=0.00..11.56 rows=4 width=68) (actual time=0.007..0.012 rows=1 loops=1)
   ->  Seq Scan on users_0  (cost=0.00..1.14 rows=1 width=68) (actual time=0.005..0.005 rows=0 loops=1)
         Filter: (id = LEAST(15, 20))
         Rows Removed by Filter: 9
   ->  Seq Scan on users_1  (cost=0.00..1.15 rows=1 width=68) (actual time=0.002..0.002 rows=1 loops=1)
         Filter: (id = LEAST(15, 20))
         Rows Removed by Filter: 9
   ->  Seq Scan on users_2  (cost=0.00..1.09 rows=1 width=68) (actual time=0.001..0.001 rows=0 loops=1)
         Filter: (id = LEAST(15, 20))
         Rows Removed by Filter: 6
   ->  Index Scan using users_3_pkey on users_3  (cost=0.15..8.17 rows=1 width=68) (actual time=0.003..0.003 rows=0 loops=1)
         Index Cond: (id = LEAST(15, 20))
 Planning Time: 0.395 ms
 Execution Time: 0.122 ms
(14 rows)

But now, I get much nicer:

                                               QUERY PLAN
--------------------------------------------------------------------------------------------------------
 Append  (cost=0.00..11.56 rows=4 width=68) (actual time=0.016..0.019 rows=1 loops=1)
   Subplans Removed: 3
   ->  Seq Scan on users_1  (cost=0.00..1.15 rows=1 width=68) (actual time=0.015..0.017 rows=1 loops=1)
         Filter: (id = LEAST(15, 20))
         Rows Removed by Filter: 9
 Planning Time: 1.949 ms
 Execution Time: 0.435 ms
(7 rows)

Of course my example is very simplistic, but I hope it shows exactly what is the new hotness and what is the benefit of it.

Thanks a lot 🙂

Leave a comment