Fix runtime partition pruning for HASH partitioned tables
authorDavid Rowley <[email protected]>
Thu, 12 Oct 2023 12:15:12 +0000 (01:15 +1300)
committerDavid Rowley <[email protected]>
Thu, 12 Oct 2023 12:15:12 +0000 (01:15 +1300)
This could only affect HASH partitioned tables with at least 2 partition
key columns.

If partition pruning was delayed until execution and the query contained
an IS NULL qual on one of the partitioned keys, and some subsequent
partitioned key was being compared to a non-Const, then this could result
in a crash due to the incorrect keyno being used to calculate the
stateidx for the expression evaluation code.

Here we fix this by properly skipping partitioned keys which have a
nullkey set.  Effectively, this must be the same as what's going on
inside perform_pruning_base_step().

Sergei Glukhov also provided a patch, but that's not what's being used
here.

Reported-by: Sergei Glukhov
Reviewed-by: tender wang, Sergei Glukhov
Discussion: https://postgr.es/m/d05b26fa-af54-27e1-f693-6c31590802fa@postgrespro.ru
Backpatch-through: 11, where runtime partition pruning was added.

src/backend/executor/execPartition.c

index b667d03c7ffae747f5be2c83d354b128e801c80f..d4c6833650da888aaa55e6b53d8e94efaaf56989 100644 (file)
@@ -1673,7 +1673,7 @@ ExecInitPruningContext(PartitionPruneContext *context,
    foreach(lc, pruning_steps)
    {
        PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc);
-       ListCell   *lc2;
+       ListCell   *lc2 = list_head(step->exprs);
        int         keyno;
 
        /* not needed for other step kinds */
@@ -1682,22 +1682,27 @@ ExecInitPruningContext(PartitionPruneContext *context,
 
        Assert(list_length(step->exprs) <= partnatts);
 
-       keyno = 0;
-       foreach(lc2, step->exprs)
+       for (keyno = 0; keyno < partnatts; keyno++)
        {
-           Expr       *expr = (Expr *) lfirst(lc2);
+           if (bms_is_member(keyno, step->nullkeys))
+               continue;
 
-           /* not needed for Consts */
-           if (!IsA(expr, Const))
+           if (lc2 != NULL)
            {
-               int         stateidx = PruneCxtStateIdx(partnatts,
-                                                       step->step.step_id,
-                                                       keyno);
+               Expr *expr = lfirst(lc2);
+
+               /* not needed for Consts */
+               if (!IsA(expr, Const))
+               {
+                   int         stateidx = PruneCxtStateIdx(partnatts,
+                                                           step->step.step_id,
+                                                           keyno);
 
-               context->exprstates[stateidx] =
-                   ExecInitExpr(expr, context->planstate);
+                   context->exprstates[stateidx] =
+                       ExecInitExpr(expr, context->planstate);
+               }
+               lc2 = lnext(lc2);
            }
-           keyno++;
        }
    }
 }