Skip to content

Commit be3d900

Browse files
committed
Fix run-time partition pruning code to handle NULL values properly.
The previous coding just ignored pruning constraints that compare a partition key to a null-valued expression. This is silly, since really what we can do there is conclude that all partitions are rejected: the pruning operator is known strict so the comparison must always fail. This also fixes the logic to not ignore constisnull for a Const comparison value. That's probably an unreachable case, since the planner would normally have simplified away a strict operator with a constant-null input. But this code has no business assuming that. David Rowley, per a gripe from me Discussion: https://postgr.es/m/26279.1528670981@sss.pgh.pa.us
1 parent 387543f commit be3d900

File tree

3 files changed

+53
-13
lines changed

3 files changed

+53
-13
lines changed

src/backend/partitioning/partprune.c

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ static PruneStepResult *perform_pruning_combine_step(PartitionPruneContext *cont
170170
static bool match_boolean_partition_clause(Oid partopfamily, Expr *clause,
171171
Expr *partkey, Expr **outconst);
172172
static bool partkey_datum_from_expr(PartitionPruneContext *context,
173-
Expr *expr, int stateidx, Datum *value);
173+
Expr *expr, int stateidx,
174+
Datum *value, bool *isnull);
174175

175176

176177
/*
@@ -184,8 +185,9 @@ static bool partkey_datum_from_expr(PartitionPruneContext *context,
184185
* indexes.
185186
*
186187
* If no non-Const expressions are being compared to the partition key in any
187-
* of the 'partitioned_rels', then we return NIL. In such a case run-time
188-
* partition pruning would be useless, since the planner did it already.
188+
* of the 'partitioned_rels', then we return NIL to indicate no run-time
189+
* pruning should be performed. Run-time pruning would be useless, since the
190+
* pruning done during planning will have pruned everything that can be.
189191
*/
190192
List *
191193
make_partition_pruneinfo(PlannerInfo *root, List *partition_rels,
@@ -2835,14 +2837,33 @@ perform_pruning_base_step(PartitionPruneContext *context,
28352837
Expr *expr;
28362838
int stateidx;
28372839
Datum datum;
2840+
bool isnull;
28382841

28392842
expr = lfirst(lc1);
28402843
stateidx = PruneCxtStateIdx(context->partnatts,
28412844
opstep->step.step_id, keyno);
2842-
if (partkey_datum_from_expr(context, expr, stateidx, &datum))
2845+
if (partkey_datum_from_expr(context, expr, stateidx,
2846+
&datum, &isnull))
28432847
{
28442848
Oid cmpfn;
28452849

2850+
/*
2851+
* Since we only allow strict operators in pruning steps, any
2852+
* null-valued comparison value must cause the comparison to
2853+
* fail, so that no partitions could match.
2854+
*/
2855+
if (isnull)
2856+
{
2857+
PruneStepResult *result;
2858+
2859+
result = (PruneStepResult *) palloc(sizeof(PruneStepResult));
2860+
result->bound_offsets = NULL;
2861+
result->scan_default = false;
2862+
result->scan_null = false;
2863+
2864+
return result;
2865+
}
2866+
28462867
/*
28472868
* If we're going to need a different comparison function than
28482869
* the one cached in the PartitionKey, we'll need to look up
@@ -3072,8 +3093,8 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
30723093
* Evaluate expression for potential partition pruning
30733094
*
30743095
* Evaluate 'expr', whose ExprState is stateidx of the context exprstate
3075-
* array; set *value to the resulting Datum. Return true if evaluation was
3076-
* possible, otherwise false.
3096+
* array; set *value and *isnull to the resulting Datum and nullflag.
3097+
* Return true if evaluation was possible, otherwise false.
30773098
*
30783099
* Note that the evaluated result may be in the per-tuple memory context of
30793100
* context->planstate->ps_ExprContext, and we may have leaked other memory
@@ -3082,11 +3103,16 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
30823103
*/
30833104
static bool
30843105
partkey_datum_from_expr(PartitionPruneContext *context,
3085-
Expr *expr, int stateidx, Datum *value)
3106+
Expr *expr, int stateidx,
3107+
Datum *value, bool *isnull)
30863108
{
30873109
if (IsA(expr, Const))
30883110
{
3089-
*value = ((Const *) expr)->constvalue;
3111+
/* We can always determine the value of a constant */
3112+
Const *con = (Const *) expr;
3113+
3114+
*value = con->constvalue;
3115+
*isnull = con->constisnull;
30903116
return true;
30913117
}
30923118
else
@@ -3105,14 +3131,10 @@ partkey_datum_from_expr(PartitionPruneContext *context,
31053131
{
31063132
ExprState *exprstate;
31073133
ExprContext *ectx;
3108-
bool isNull;
31093134

31103135
exprstate = context->exprstates[stateidx];
31113136
ectx = context->planstate->ps_ExprContext;
3112-
*value = ExecEvalExprSwitchContext(exprstate, ectx, &isNull);
3113-
if (isNull)
3114-
return false;
3115-
3137+
*value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);
31163138
return true;
31173139
}
31183140
}

src/test/regress/expected/partition_prune.out

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2731,6 +2731,20 @@ explain (analyze, costs off, summary off, timing off) execute q1 (1,2,2,1);
27312731
Filter: ((b = ANY (ARRAY[$1, $2])) AND ($3 <> b) AND ($4 <> b))
27322732
(4 rows)
27332733

2734+
-- Ensure Params that evaluate to NULL properly prune away all partitions
2735+
explain (analyze, costs off, summary off, timing off)
2736+
select * from listp where a = (select null::int);
2737+
QUERY PLAN
2738+
----------------------------------------------
2739+
Append (actual rows=0 loops=1)
2740+
InitPlan 1 (returns $0)
2741+
-> Result (actual rows=1 loops=1)
2742+
-> Seq Scan on listp_1_1 (never executed)
2743+
Filter: (a = $0)
2744+
-> Seq Scan on listp_2_1 (never executed)
2745+
Filter: (a = $0)
2746+
(7 rows)
2747+
27342748
drop table listp;
27352749
-- Ensure runtime pruning works with initplans params with boolean types
27362750
create table boolvalues (value bool not null);

src/test/regress/sql/partition_prune.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,10 @@ explain (analyze, costs off, summary off, timing off) execute q1 (1,2,2,0);
685685
-- One subplan will remain in this case, but it should not be executed.
686686
explain (analyze, costs off, summary off, timing off) execute q1 (1,2,2,1);
687687

688+
-- Ensure Params that evaluate to NULL properly prune away all partitions
689+
explain (analyze, costs off, summary off, timing off)
690+
select * from listp where a = (select null::int);
691+
688692
drop table listp;
689693

690694
-- Ensure runtime pruning works with initplans params with boolean types

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy