summaryrefslogtreecommitdiff
path: root/src/backend/partitioning
diff options
context:
space:
mode:
authorDavid Rowley2024-02-19 23:49:37 +0000
committerDavid Rowley2024-02-19 23:49:37 +0000
commit4c2369ac5d0a108df4d65a2886657efa010d67ca (patch)
tree3597729d9e868a50834196e36c3aa9c196372ed7 /src/backend/partitioning
parent0e162810df7657bac24ba4657460a87104523fc6 (diff)
Fix incorrect pruning of NULL partition for boolean IS NOT clauses
Partition pruning wrongly assumed that, for a table partitioned on a boolean column, a clause in the form "boolcol IS NOT false" and "boolcol IS NOT true" could be inverted to correspondingly become "boolcol IS true" and "boolcol IS false". These are not equivalent as the NOT version matches the opposite boolean value *and* NULLs. This incorrect assumption meant that partition pruning pruned away partitions that could contain NULL values. Here we fix this by correctly not pruning partitions which could store NULLs. To be affected by this, the table must be partitioned by a NULLable boolean column and queries would have to contain "boolcol IS NOT false" or "boolcol IS NOT true". This could result in queries filtering out NULL values with a LIST partitioned table and "ERROR: invalid strategy number 0" for RANGE and HASH partitioned tables. Reported-by: Alexander Lakhin Bug: #18344 Discussion: https://postgr.es/m/[email protected] Backpatch-through: 12
Diffstat (limited to 'src/backend/partitioning')
-rw-r--r--src/backend/partitioning/partprune.c56
1 files changed, 54 insertions, 2 deletions
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index 6b635e8ad16..ccb9a579b30 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -1810,11 +1810,63 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
{
PartClauseInfo *partclause;
+ /*
+ * For bool tests in the form of partkey IS NOT true and IS NOT false,
+ * we invert these clauses. Effectively, "partkey IS NOT true"
+ * becomes "partkey IS false OR partkey IS NULL". We do this by
+ * building an OR BoolExpr and forming a clause just like that and
+ * punt it off to gen_partprune_steps_internal() to generate pruning
+ * steps.
+ */
+ if (noteq)
+ {
+ List *new_clauses;
+ List *or_clause;
+ BooleanTest *new_booltest = (BooleanTest *) copyObject(clause);
+ NullTest *nulltest;
+
+ /* We expect 'noteq' to only be set to true for BooleanTests */
+ Assert(IsA(clause, BooleanTest));
+
+ /* reverse the bool test */
+ if (new_booltest->booltesttype == IS_NOT_TRUE)
+ new_booltest->booltesttype = IS_FALSE;
+ else if (new_booltest->booltesttype == IS_NOT_FALSE)
+ new_booltest->booltesttype = IS_TRUE;
+ else
+ {
+ /*
+ * We only expect match_boolean_partition_clause to match for
+ * IS_NOT_TRUE and IS_NOT_FALSE. IS_NOT_UNKNOWN is not
+ * supported.
+ */
+ Assert(false);
+ }
+
+ nulltest = makeNode(NullTest);
+ nulltest->arg = copyObject(partkey);
+ nulltest->nulltesttype = IS_NULL;
+ nulltest->argisrow = false;
+ nulltest->location = -1;
+
+ new_clauses = list_make2(new_booltest, nulltest);
+ or_clause = list_make1(makeBoolExpr(OR_EXPR, new_clauses, -1));
+
+ /* Finally, generate steps */
+ *clause_steps = gen_partprune_steps_internal(context, or_clause);
+
+ if (context->contradictory)
+ return PARTCLAUSE_MATCH_CONTRADICT; /* shouldn't happen */
+ else if (*clause_steps == NIL)
+ return PARTCLAUSE_UNSUPPORTED; /* step generation failed */
+ return PARTCLAUSE_MATCH_STEPS;
+ }
+
partclause = (PartClauseInfo *) palloc(sizeof(PartClauseInfo));
partclause->keyno = partkeyidx;
/* Do pruning with the Boolean equality operator. */
partclause->opno = BooleanEqualOperator;
- partclause->op_is_ne = noteq;
+ partclause->op_is_ne = false;
partclause->expr = expr;
/* We know that expr is of Boolean type. */
partclause->cmpfn = part_scheme->partsupfunc[partkeyidx].fn_oid;
@@ -2358,7 +2410,7 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
* For LIST and RANGE partitioned tables, callers must ensure that
* step_nullkeys is NULL, and that prefix contains at least one clause for
* each of the partition keys prior to the key that 'step_lastexpr' and
- * 'step_lastcmpfn'belong to.
+ * 'step_lastcmpfn' belong to.
*
* For HASH partitioned tables, callers must ensure that 'prefix' contains at
* least one clause for each of the partition keys apart from the final key