mirror of
https://github.com/postgres/postgres.git
synced 2025-08-28 18:48:04 +03:00
Fix hash partition pruning with asymmetric partition sets.
perform_pruning_combine_step() was not taught about the number of partition indexes used in hash partitioning; more embarrassingly, get_matching_hash_bounds() also had it wrong. These errors are masked in the common case where all the partitions have the same modulus and no partition is missing. However, with missing or unequal-size partitions, we could erroneously prune some partitions that need to be scanned, leading to silently wrong query answers. While a minimal-footprint fix for this could be to export get_partition_bound_num_indexes and make the incorrect functions use it, I'm of the opinion that that function should never have existed in the first place. It's not reasonable data structure design that PartitionBoundInfoData lacks any explicit record of the length of its indexes[] array. Perhaps that was all right when it could always be assumed equal to ndatums, but something should have been done about it as soon as that stopped being true. Putting in an explicit "nindexes" field makes both partition_bounds_equal() and partition_bounds_copy() simpler, safer, and faster than before, and removes explicit knowledge of the number-of-partition-indexes rules from some other places too. This change also makes get_hash_partition_greatest_modulus obsolete. I left that in place in case any external code uses it, but no core code does anymore. Per bug #16840 from Michał Albrycht. Back-patch to v11 where the hash partitioning code came in. (In the back branches, add the new field at the end of PartitionBoundInfoData to minimize ABI risks.) Discussion: https://postgr.es/m/16840-571a22976f829ad4@postgresql.org
This commit is contained in:
@@ -781,7 +781,10 @@ get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
|
||||
scan_default = final_result->scan_default;
|
||||
while ((i = bms_next_member(final_result->bound_offsets, i)) >= 0)
|
||||
{
|
||||
int partindex = context->boundinfo->indexes[i];
|
||||
int partindex;
|
||||
|
||||
Assert(i < context->boundinfo->nindexes);
|
||||
partindex = context->boundinfo->indexes[i];
|
||||
|
||||
if (partindex < 0)
|
||||
{
|
||||
@@ -2514,20 +2517,19 @@ get_matching_hash_bounds(PartitionPruneContext *context,
|
||||
for (i = 0; i < partnatts; i++)
|
||||
isnull[i] = bms_is_member(i, nullkeys);
|
||||
|
||||
greatest_modulus = get_hash_partition_greatest_modulus(boundinfo);
|
||||
rowHash = compute_partition_hash_value(partnatts, partsupfunc, partcollation,
|
||||
values, isnull);
|
||||
|
||||
greatest_modulus = boundinfo->nindexes;
|
||||
if (partindices[rowHash % greatest_modulus] >= 0)
|
||||
result->bound_offsets =
|
||||
bms_make_singleton(rowHash % greatest_modulus);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Getting here means at least one hash partition exists. */
|
||||
Assert(boundinfo->ndatums > 0);
|
||||
/* Report all valid offsets into the boundinfo->indexes array. */
|
||||
result->bound_offsets = bms_add_range(NULL, 0,
|
||||
boundinfo->ndatums - 1);
|
||||
boundinfo->nindexes - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3388,30 +3390,20 @@ perform_pruning_combine_step(PartitionPruneContext *context,
|
||||
PartitionPruneStepCombine *cstep,
|
||||
PruneStepResult **step_results)
|
||||
{
|
||||
ListCell *lc1;
|
||||
PruneStepResult *result = NULL;
|
||||
PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
|
||||
bool firststep;
|
||||
ListCell *lc1;
|
||||
|
||||
/*
|
||||
* A combine step without any source steps is an indication to not perform
|
||||
* any partition pruning. Return all datum indexes in that case.
|
||||
*/
|
||||
result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
|
||||
if (list_length(cstep->source_stepids) == 0)
|
||||
if (cstep->source_stepids == NIL)
|
||||
{
|
||||
PartitionBoundInfo boundinfo = context->boundinfo;
|
||||
int rangemax;
|
||||
|
||||
/*
|
||||
* Add all valid offsets into the boundinfo->indexes array. For range
|
||||
* partitioning, boundinfo->indexes contains (boundinfo->ndatums + 1)
|
||||
* valid entries; otherwise there are boundinfo->ndatums.
|
||||
*/
|
||||
rangemax = context->strategy == PARTITION_STRATEGY_RANGE ?
|
||||
boundinfo->ndatums : boundinfo->ndatums - 1;
|
||||
|
||||
result->bound_offsets =
|
||||
bms_add_range(result->bound_offsets, 0, rangemax);
|
||||
bms_add_range(NULL, 0, boundinfo->nindexes - 1);
|
||||
result->scan_default = partition_bound_has_default(boundinfo);
|
||||
result->scan_null = partition_bound_accepts_nulls(boundinfo);
|
||||
return result;
|
||||
|
Reference in New Issue
Block a user