Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)