Commit 9aef1731 authored by Robert Haas's avatar Robert Haas

Refactor code for partition bound searching

Remove partition_bound_cmp() and partition_bound_bsearch(), whose
void * argument could be, depending on the situation, of any of
three different types: PartitionBoundSpec *, PartitionRangeBound *,
Datum *.

Instead, introduce separate bound-searching functions for each
situation: partition_list_bsearch, partition_range_bsearch,
partition_range_datum_bsearch, and partition_hash_bsearch.  This
requires duplicating the code for binary search, but it makes the
code much more type safe, involves fewer branches at runtime, and
at least in my opinion, is much easier to understand.

Along the way, add an option to partition_range_datum_bsearch
allowing the number of keys to be specified, so that we can search
for partitions based on a prefix of the full list of partition
keys.  This is important for pending work to improve partition
pruning.

Amit Langote, per a suggestion from me.

Discussion: http://postgr.es/m/CA+TgmoaVLDLc8=YESRwD32gPhodU_ELmXyKs77gveiYp+JE4vQ@mail.gmail.com
parent 9222c0d9
...@@ -170,14 +170,21 @@ static int32 partition_rbound_cmp(PartitionKey key, ...@@ -170,14 +170,21 @@ static int32 partition_rbound_cmp(PartitionKey key,
bool lower1, PartitionRangeBound *b2); bool lower1, PartitionRangeBound *b2);
static int32 partition_rbound_datum_cmp(PartitionKey key, static int32 partition_rbound_datum_cmp(PartitionKey key,
Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
Datum *tuple_datums); Datum *tuple_datums, int n_tuple_datums);
static int32 partition_bound_cmp(PartitionKey key, static int partition_list_bsearch(PartitionKey key,
PartitionBoundInfo boundinfo, PartitionBoundInfo boundinfo,
int offset, void *probe, bool probe_is_bound); Datum value, bool *is_equal);
static int partition_bound_bsearch(PartitionKey key, static int partition_range_bsearch(PartitionKey key,
PartitionBoundInfo boundinfo, PartitionBoundInfo boundinfo,
void *probe, bool probe_is_bound, bool *is_equal); PartitionRangeBound *probe, bool *is_equal);
static int partition_range_datum_bsearch(PartitionKey key,
PartitionBoundInfo boundinfo,
int nvalues, Datum *values, bool *is_equal);
static int partition_hash_bsearch(PartitionKey key,
PartitionBoundInfo boundinfo,
int modulus, int remainder);
static int get_partition_bound_num_indexes(PartitionBoundInfo b); static int get_partition_bound_num_indexes(PartitionBoundInfo b);
static int get_greatest_modulus(PartitionBoundInfo b); static int get_greatest_modulus(PartitionBoundInfo b);
static uint64 compute_hash_value(PartitionKey key, Datum *values, bool *isnull); static uint64 compute_hash_value(PartitionKey key, Datum *values, bool *isnull);
...@@ -981,8 +988,7 @@ check_new_partition_bound(char *relname, Relation parent, ...@@ -981,8 +988,7 @@ check_new_partition_bound(char *relname, Relation parent,
int greatest_modulus; int greatest_modulus;
int remainder; int remainder;
int offset; int offset;
bool equal, bool valid_modulus = true;
valid_modulus = true;
int prev_modulus, /* Previous largest modulus */ int prev_modulus, /* Previous largest modulus */
next_modulus; /* Next largest modulus */ next_modulus; /* Next largest modulus */
...@@ -995,12 +1001,13 @@ check_new_partition_bound(char *relname, Relation parent, ...@@ -995,12 +1001,13 @@ check_new_partition_bound(char *relname, Relation parent,
* modulus 10 and a partition with modulus 15, because 10 * modulus 10 and a partition with modulus 15, because 10
* is not a factor of 15. * is not a factor of 15.
* *
* Get greatest bound in array boundinfo->datums which is * Get the greatest (modulus, remainder) pair contained in
* less than or equal to spec->modulus and * boundinfo->datums that is less than or equal to the
* spec->remainder. * (spec->modulus, spec->remainder) pair.
*/ */
offset = partition_bound_bsearch(key, boundinfo, spec, offset = partition_hash_bsearch(key, boundinfo,
true, &equal); spec->modulus,
spec->remainder);
if (offset < 0) if (offset < 0)
{ {
next_modulus = DatumGetInt32(datums[0][0]); next_modulus = DatumGetInt32(datums[0][0]);
...@@ -1074,9 +1081,9 @@ check_new_partition_bound(char *relname, Relation parent, ...@@ -1074,9 +1081,9 @@ check_new_partition_bound(char *relname, Relation parent,
int offset; int offset;
bool equal; bool equal;
offset = partition_bound_bsearch(key, boundinfo, offset = partition_list_bsearch(key, boundinfo,
&val->constvalue, val->constvalue,
true, &equal); &equal);
if (offset >= 0 && equal) if (offset >= 0 && equal)
{ {
overlap = true; overlap = true;
...@@ -1148,8 +1155,8 @@ check_new_partition_bound(char *relname, Relation parent, ...@@ -1148,8 +1155,8 @@ check_new_partition_bound(char *relname, Relation parent,
* since the index array is initialised with an extra -1 * since the index array is initialised with an extra -1
* at the end. * at the end.
*/ */
offset = partition_bound_bsearch(key, boundinfo, lower, offset = partition_range_bsearch(key, boundinfo, lower,
true, &equal); &equal);
if (boundinfo->indexes[offset + 1] < 0) if (boundinfo->indexes[offset + 1] < 0)
{ {
...@@ -1162,10 +1169,16 @@ check_new_partition_bound(char *relname, Relation parent, ...@@ -1162,10 +1169,16 @@ check_new_partition_bound(char *relname, Relation parent,
if (offset + 1 < boundinfo->ndatums) if (offset + 1 < boundinfo->ndatums)
{ {
int32 cmpval; int32 cmpval;
Datum *datums;
PartitionRangeDatumKind *kind;
bool is_lower;
datums = boundinfo->datums[offset + 1];
kind = boundinfo->kind[offset + 1];
is_lower = (boundinfo->indexes[offset + 1] == -1);
cmpval = partition_bound_cmp(key, boundinfo, cmpval = partition_rbound_cmp(key, datums, kind,
offset + 1, upper, is_lower, upper);
true);
if (cmpval < 0) if (cmpval < 0)
{ {
/* /*
...@@ -2574,11 +2587,9 @@ get_partition_for_tuple(Relation relation, Datum *values, bool *isnull) ...@@ -2574,11 +2587,9 @@ get_partition_for_tuple(Relation relation, Datum *values, bool *isnull)
{ {
bool equal = false; bool equal = false;
bound_offset = partition_bound_bsearch(key, bound_offset = partition_list_bsearch(key,
partdesc->boundinfo, partdesc->boundinfo,
values, values[0], &equal);
false,
&equal);
if (bound_offset >= 0 && equal) if (bound_offset >= 0 && equal)
part_index = partdesc->boundinfo->indexes[bound_offset]; part_index = partdesc->boundinfo->indexes[bound_offset];
} }
...@@ -2605,12 +2616,11 @@ get_partition_for_tuple(Relation relation, Datum *values, bool *isnull) ...@@ -2605,12 +2616,11 @@ get_partition_for_tuple(Relation relation, Datum *values, bool *isnull)
if (!range_partkey_has_null) if (!range_partkey_has_null)
{ {
bound_offset = partition_bound_bsearch(key, bound_offset = partition_range_datum_bsearch(key,
partdesc->boundinfo, partdesc->boundinfo,
values, key->partnatts,
false, values,
&equal); &equal);
/* /*
* The bound at bound_offset is less than or equal to the * The bound at bound_offset is less than or equal to the
* tuple value, so the bound at offset+1 is the upper * tuple value, so the bound at offset+1 is the upper
...@@ -2881,12 +2891,12 @@ partition_rbound_cmp(PartitionKey key, ...@@ -2881,12 +2891,12 @@ partition_rbound_cmp(PartitionKey key,
static int32 static int32
partition_rbound_datum_cmp(PartitionKey key, partition_rbound_datum_cmp(PartitionKey key,
Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
Datum *tuple_datums) Datum *tuple_datums, int n_tuple_datums)
{ {
int i; int i;
int32 cmpval = -1; int32 cmpval = -1;
for (i = 0; i < key->partnatts; i++) for (i = 0; i < n_tuple_datums; i++)
{ {
if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE) if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
return -1; return -1;
...@@ -2905,84 +2915,104 @@ partition_rbound_datum_cmp(PartitionKey key, ...@@ -2905,84 +2915,104 @@ partition_rbound_datum_cmp(PartitionKey key,
} }
/* /*
* partition_bound_cmp * partition_list_bsearch
* Returns the index of the greatest bound datum that is less than equal
* to the given value or -1 if all of the bound datums are greater
* *
* Return whether the bound at offset in boundinfo is <, =, or > the argument * *is_equal is set to true if the bound datum at the returned index is equal
* specified in *probe. * to the input value.
*/ */
static int32 static int
partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo, partition_list_bsearch(PartitionKey key,
int offset, void *probe, bool probe_is_bound) PartitionBoundInfo boundinfo,
Datum value, bool *is_equal)
{ {
Datum *bound_datums = boundinfo->datums[offset]; int lo,
int32 cmpval = -1; hi,
mid;
switch (key->strategy) lo = -1;
hi = boundinfo->ndatums - 1;
while (lo < hi)
{ {
case PARTITION_STRATEGY_HASH: int32 cmpval;
{
PartitionBoundSpec *spec = (PartitionBoundSpec *) probe;
cmpval = partition_hbound_cmp(DatumGetInt32(bound_datums[0]), mid = (lo + hi + 1) / 2;
DatumGetInt32(bound_datums[1]), cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
spec->modulus, spec->remainder); key->partcollation[0],
boundinfo->datums[mid][0],
value));
if (cmpval <= 0)
{
lo = mid;
*is_equal = (cmpval == 0);
if (*is_equal)
break; break;
} }
case PARTITION_STRATEGY_LIST: else
cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0], hi = mid - 1;
key->partcollation[0], }
bound_datums[0],
*(Datum *) probe));
break;
case PARTITION_STRATEGY_RANGE: return lo;
{ }
PartitionRangeDatumKind *kind = boundinfo->kind[offset];
if (probe_is_bound) /*
{ * partition_range_bsearch
/* * Returns the index of the greatest range bound that is less than or
* We need to pass whether the existing bound is a lower * equal to the given range bound or -1 if all of the range bounds are
* bound, so that two equal-valued lower and upper bounds * greater
* are not regarded equal. *
*/ * *is_equal is set to true if the range bound at the returned index is equal
bool lower = boundinfo->indexes[offset] < 0; * to the input range bound
*/
static int
partition_range_bsearch(PartitionKey key,
PartitionBoundInfo boundinfo,
PartitionRangeBound *probe, bool *is_equal)
{
int lo,
hi,
mid;
cmpval = partition_rbound_cmp(key, lo = -1;
bound_datums, kind, lower, hi = boundinfo->ndatums - 1;
(PartitionRangeBound *) probe); while (lo < hi)
} {
else int32 cmpval;
cmpval = partition_rbound_datum_cmp(key,
bound_datums, kind,
(Datum *) probe);
break;
}
default: mid = (lo + hi + 1) / 2;
elog(ERROR, "unexpected partition strategy: %d", cmpval = partition_rbound_cmp(key,
(int) key->strategy); boundinfo->datums[mid],
boundinfo->kind[mid],
(boundinfo->indexes[mid] == -1),
probe);
if (cmpval <= 0)
{
lo = mid;
*is_equal = (cmpval == 0);
if (*is_equal)
break;
}
else
hi = mid - 1;
} }
return cmpval; return lo;
} }
/* /*
* Binary search on a collection of partition bounds. Returns greatest * partition_range_bsearch
* bound in array boundinfo->datums which is less than or equal to *probe. * Returns the index of the greatest range bound that is less than or
* If all bounds in the array are greater than *probe, -1 is returned. * equal to the given tuple or -1 if all of the range bounds are greater
*
* *probe could either be a partition bound or a Datum array representing
* the partition key of a tuple being routed; probe_is_bound tells which.
* We pass that down to the comparison function so that it can interpret the
* contents of *probe accordingly.
* *
* *is_equal is set to whether the bound at the returned index is equal with * *is_equal is set to true if the range bound at the returned index is equal
* *probe. * to the input tuple.
*/ */
static int static int
partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo, partition_range_datum_bsearch(PartitionKey key,
void *probe, bool probe_is_bound, bool *is_equal) PartitionBoundInfo boundinfo,
int nvalues, Datum *values, bool *is_equal)
{ {
int lo, int lo,
hi, hi,
...@@ -2995,8 +3025,11 @@ partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo, ...@@ -2995,8 +3025,11 @@ partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo,
int32 cmpval; int32 cmpval;
mid = (lo + hi + 1) / 2; mid = (lo + hi + 1) / 2;
cmpval = partition_bound_cmp(key, boundinfo, mid, probe, cmpval = partition_rbound_datum_cmp(key,
probe_is_bound); boundinfo->datums[mid],
boundinfo->kind[mid],
values,
nvalues);
if (cmpval <= 0) if (cmpval <= 0)
{ {
lo = mid; lo = mid;
...@@ -3012,6 +3045,48 @@ partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo, ...@@ -3012,6 +3045,48 @@ partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo,
return lo; return lo;
} }
/*
* partition_hash_bsearch
* Returns the index of the greatest (modulus, remainder) pair that is
* less than or equal to the given (modulus, remainder) pair or -1 if
* all of them are greater
*/
static int
partition_hash_bsearch(PartitionKey key,
PartitionBoundInfo boundinfo,
int modulus, int remainder)
{
int lo,
hi,
mid;
lo = -1;
hi = boundinfo->ndatums - 1;
while (lo < hi)
{
int32 cmpval,
bound_modulus,
bound_remainder;
mid = (lo + hi + 1) / 2;
bound_modulus = DatumGetInt32(boundinfo->datums[mid][0]);
bound_remainder = DatumGetInt32(boundinfo->datums[mid][1]);
cmpval = partition_hbound_cmp(bound_modulus, bound_remainder,
modulus, remainder);
if (cmpval <= 0)
{
lo = mid;
if (cmpval == 0)
break;
}
else
hi = mid - 1;
}
return lo;
}
/* /*
* get_default_oid_from_partdesc * get_default_oid_from_partdesc
* *
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment