Skip to content

Commit 9aef173

Browse files
committed
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
1 parent 9222c0d commit 9aef173

File tree

1 file changed

+170
-95
lines changed

1 file changed

+170
-95
lines changed

src/backend/catalog/partition.c

+170-95
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,21 @@ static int32 partition_rbound_cmp(PartitionKey key,
170170
bool lower1, PartitionRangeBound *b2);
171171
static int32 partition_rbound_datum_cmp(PartitionKey key,
172172
Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
173-
Datum *tuple_datums);
173+
Datum *tuple_datums, int n_tuple_datums);
174174

175-
static int32 partition_bound_cmp(PartitionKey key,
176-
PartitionBoundInfo boundinfo,
177-
int offset, void *probe, bool probe_is_bound);
178-
static int partition_bound_bsearch(PartitionKey key,
175+
static int partition_list_bsearch(PartitionKey key,
176+
PartitionBoundInfo boundinfo,
177+
Datum value, bool *is_equal);
178+
static int partition_range_bsearch(PartitionKey key,
179179
PartitionBoundInfo boundinfo,
180-
void *probe, bool probe_is_bound, bool *is_equal);
180+
PartitionRangeBound *probe, bool *is_equal);
181+
static int partition_range_datum_bsearch(PartitionKey key,
182+
PartitionBoundInfo boundinfo,
183+
int nvalues, Datum *values, bool *is_equal);
184+
static int partition_hash_bsearch(PartitionKey key,
185+
PartitionBoundInfo boundinfo,
186+
int modulus, int remainder);
187+
181188
static int get_partition_bound_num_indexes(PartitionBoundInfo b);
182189
static int get_greatest_modulus(PartitionBoundInfo b);
183190
static uint64 compute_hash_value(PartitionKey key, Datum *values, bool *isnull);
@@ -981,8 +988,7 @@ check_new_partition_bound(char *relname, Relation parent,
981988
int greatest_modulus;
982989
int remainder;
983990
int offset;
984-
bool equal,
985-
valid_modulus = true;
991+
bool valid_modulus = true;
986992
int prev_modulus, /* Previous largest modulus */
987993
next_modulus; /* Next largest modulus */
988994

@@ -995,12 +1001,13 @@ check_new_partition_bound(char *relname, Relation parent,
9951001
* modulus 10 and a partition with modulus 15, because 10
9961002
* is not a factor of 15.
9971003
*
998-
* Get greatest bound in array boundinfo->datums which is
999-
* less than or equal to spec->modulus and
1000-
* spec->remainder.
1004+
* Get the greatest (modulus, remainder) pair contained in
1005+
* boundinfo->datums that is less than or equal to the
1006+
* (spec->modulus, spec->remainder) pair.
10011007
*/
1002-
offset = partition_bound_bsearch(key, boundinfo, spec,
1003-
true, &equal);
1008+
offset = partition_hash_bsearch(key, boundinfo,
1009+
spec->modulus,
1010+
spec->remainder);
10041011
if (offset < 0)
10051012
{
10061013
next_modulus = DatumGetInt32(datums[0][0]);
@@ -1074,9 +1081,9 @@ check_new_partition_bound(char *relname, Relation parent,
10741081
int offset;
10751082
bool equal;
10761083

1077-
offset = partition_bound_bsearch(key, boundinfo,
1078-
&val->constvalue,
1079-
true, &equal);
1084+
offset = partition_list_bsearch(key, boundinfo,
1085+
val->constvalue,
1086+
&equal);
10801087
if (offset >= 0 && equal)
10811088
{
10821089
overlap = true;
@@ -1148,8 +1155,8 @@ check_new_partition_bound(char *relname, Relation parent,
11481155
* since the index array is initialised with an extra -1
11491156
* at the end.
11501157
*/
1151-
offset = partition_bound_bsearch(key, boundinfo, lower,
1152-
true, &equal);
1158+
offset = partition_range_bsearch(key, boundinfo, lower,
1159+
&equal);
11531160

11541161
if (boundinfo->indexes[offset + 1] < 0)
11551162
{
@@ -1162,10 +1169,16 @@ check_new_partition_bound(char *relname, Relation parent,
11621169
if (offset + 1 < boundinfo->ndatums)
11631170
{
11641171
int32 cmpval;
1172+
Datum *datums;
1173+
PartitionRangeDatumKind *kind;
1174+
bool is_lower;
1175+
1176+
datums = boundinfo->datums[offset + 1];
1177+
kind = boundinfo->kind[offset + 1];
1178+
is_lower = (boundinfo->indexes[offset + 1] == -1);
11651179

1166-
cmpval = partition_bound_cmp(key, boundinfo,
1167-
offset + 1, upper,
1168-
true);
1180+
cmpval = partition_rbound_cmp(key, datums, kind,
1181+
is_lower, upper);
11691182
if (cmpval < 0)
11701183
{
11711184
/*
@@ -2574,11 +2587,9 @@ get_partition_for_tuple(Relation relation, Datum *values, bool *isnull)
25742587
{
25752588
bool equal = false;
25762589

2577-
bound_offset = partition_bound_bsearch(key,
2578-
partdesc->boundinfo,
2579-
values,
2580-
false,
2581-
&equal);
2590+
bound_offset = partition_list_bsearch(key,
2591+
partdesc->boundinfo,
2592+
values[0], &equal);
25822593
if (bound_offset >= 0 && equal)
25832594
part_index = partdesc->boundinfo->indexes[bound_offset];
25842595
}
@@ -2605,12 +2616,11 @@ get_partition_for_tuple(Relation relation, Datum *values, bool *isnull)
26052616

26062617
if (!range_partkey_has_null)
26072618
{
2608-
bound_offset = partition_bound_bsearch(key,
2609-
partdesc->boundinfo,
2610-
values,
2611-
false,
2612-
&equal);
2613-
2619+
bound_offset = partition_range_datum_bsearch(key,
2620+
partdesc->boundinfo,
2621+
key->partnatts,
2622+
values,
2623+
&equal);
26142624
/*
26152625
* The bound at bound_offset is less than or equal to the
26162626
* tuple value, so the bound at offset+1 is the upper
@@ -2881,12 +2891,12 @@ partition_rbound_cmp(PartitionKey key,
28812891
static int32
28822892
partition_rbound_datum_cmp(PartitionKey key,
28832893
Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
2884-
Datum *tuple_datums)
2894+
Datum *tuple_datums, int n_tuple_datums)
28852895
{
28862896
int i;
28872897
int32 cmpval = -1;
28882898

2889-
for (i = 0; i < key->partnatts; i++)
2899+
for (i = 0; i < n_tuple_datums; i++)
28902900
{
28912901
if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
28922902
return -1;
@@ -2905,84 +2915,104 @@ partition_rbound_datum_cmp(PartitionKey key,
29052915
}
29062916

29072917
/*
2908-
* partition_bound_cmp
2918+
* partition_list_bsearch
2919+
* Returns the index of the greatest bound datum that is less than equal
2920+
* to the given value or -1 if all of the bound datums are greater
29092921
*
2910-
* Return whether the bound at offset in boundinfo is <, =, or > the argument
2911-
* specified in *probe.
2922+
* *is_equal is set to true if the bound datum at the returned index is equal
2923+
* to the input value.
29122924
*/
2913-
static int32
2914-
partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo,
2915-
int offset, void *probe, bool probe_is_bound)
2925+
static int
2926+
partition_list_bsearch(PartitionKey key,
2927+
PartitionBoundInfo boundinfo,
2928+
Datum value, bool *is_equal)
29162929
{
2917-
Datum *bound_datums = boundinfo->datums[offset];
2918-
int32 cmpval = -1;
2930+
int lo,
2931+
hi,
2932+
mid;
29192933

2920-
switch (key->strategy)
2934+
lo = -1;
2935+
hi = boundinfo->ndatums - 1;
2936+
while (lo < hi)
29212937
{
2922-
case PARTITION_STRATEGY_HASH:
2923-
{
2924-
PartitionBoundSpec *spec = (PartitionBoundSpec *) probe;
2938+
int32 cmpval;
29252939

2926-
cmpval = partition_hbound_cmp(DatumGetInt32(bound_datums[0]),
2927-
DatumGetInt32(bound_datums[1]),
2928-
spec->modulus, spec->remainder);
2940+
mid = (lo + hi + 1) / 2;
2941+
cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
2942+
key->partcollation[0],
2943+
boundinfo->datums[mid][0],
2944+
value));
2945+
if (cmpval <= 0)
2946+
{
2947+
lo = mid;
2948+
*is_equal = (cmpval == 0);
2949+
if (*is_equal)
29292950
break;
2930-
}
2931-
case PARTITION_STRATEGY_LIST:
2932-
cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
2933-
key->partcollation[0],
2934-
bound_datums[0],
2935-
*(Datum *) probe));
2936-
break;
2951+
}
2952+
else
2953+
hi = mid - 1;
2954+
}
29372955

2938-
case PARTITION_STRATEGY_RANGE:
2939-
{
2940-
PartitionRangeDatumKind *kind = boundinfo->kind[offset];
2956+
return lo;
2957+
}
29412958

2942-
if (probe_is_bound)
2943-
{
2944-
/*
2945-
* We need to pass whether the existing bound is a lower
2946-
* bound, so that two equal-valued lower and upper bounds
2947-
* are not regarded equal.
2948-
*/
2949-
bool lower = boundinfo->indexes[offset] < 0;
2959+
/*
2960+
* partition_range_bsearch
2961+
* Returns the index of the greatest range bound that is less than or
2962+
* equal to the given range bound or -1 if all of the range bounds are
2963+
* greater
2964+
*
2965+
* *is_equal is set to true if the range bound at the returned index is equal
2966+
* to the input range bound
2967+
*/
2968+
static int
2969+
partition_range_bsearch(PartitionKey key,
2970+
PartitionBoundInfo boundinfo,
2971+
PartitionRangeBound *probe, bool *is_equal)
2972+
{
2973+
int lo,
2974+
hi,
2975+
mid;
29502976

2951-
cmpval = partition_rbound_cmp(key,
2952-
bound_datums, kind, lower,
2953-
(PartitionRangeBound *) probe);
2954-
}
2955-
else
2956-
cmpval = partition_rbound_datum_cmp(key,
2957-
bound_datums, kind,
2958-
(Datum *) probe);
2959-
break;
2960-
}
2977+
lo = -1;
2978+
hi = boundinfo->ndatums - 1;
2979+
while (lo < hi)
2980+
{
2981+
int32 cmpval;
29612982

2962-
default:
2963-
elog(ERROR, "unexpected partition strategy: %d",
2964-
(int) key->strategy);
2983+
mid = (lo + hi + 1) / 2;
2984+
cmpval = partition_rbound_cmp(key,
2985+
boundinfo->datums[mid],
2986+
boundinfo->kind[mid],
2987+
(boundinfo->indexes[mid] == -1),
2988+
probe);
2989+
if (cmpval <= 0)
2990+
{
2991+
lo = mid;
2992+
*is_equal = (cmpval == 0);
2993+
2994+
if (*is_equal)
2995+
break;
2996+
}
2997+
else
2998+
hi = mid - 1;
29652999
}
29663000

2967-
return cmpval;
3001+
return lo;
29683002
}
29693003

29703004
/*
2971-
* Binary search on a collection of partition bounds. Returns greatest
2972-
* bound in array boundinfo->datums which is less than or equal to *probe.
2973-
* If all bounds in the array are greater than *probe, -1 is returned.
2974-
*
2975-
* *probe could either be a partition bound or a Datum array representing
2976-
* the partition key of a tuple being routed; probe_is_bound tells which.
2977-
* We pass that down to the comparison function so that it can interpret the
2978-
* contents of *probe accordingly.
3005+
* partition_range_bsearch
3006+
* Returns the index of the greatest range bound that is less than or
3007+
* equal to the given tuple or -1 if all of the range bounds are greater
29793008
*
2980-
* *is_equal is set to whether the bound at the returned index is equal with
2981-
* *probe.
3009+
* *is_equal is set to true if the range bound at the returned index is equal
3010+
* to the input tuple.
29823011
*/
29833012
static int
2984-
partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo,
2985-
void *probe, bool probe_is_bound, bool *is_equal)
3013+
partition_range_datum_bsearch(PartitionKey key,
3014+
PartitionBoundInfo boundinfo,
3015+
int nvalues, Datum *values, bool *is_equal)
29863016
{
29873017
int lo,
29883018
hi,
@@ -2995,8 +3025,11 @@ partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo,
29953025
int32 cmpval;
29963026

29973027
mid = (lo + hi + 1) / 2;
2998-
cmpval = partition_bound_cmp(key, boundinfo, mid, probe,
2999-
probe_is_bound);
3028+
cmpval = partition_rbound_datum_cmp(key,
3029+
boundinfo->datums[mid],
3030+
boundinfo->kind[mid],
3031+
values,
3032+
nvalues);
30003033
if (cmpval <= 0)
30013034
{
30023035
lo = mid;
@@ -3012,6 +3045,48 @@ partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo,
30123045
return lo;
30133046
}
30143047

3048+
/*
3049+
* partition_hash_bsearch
3050+
* Returns the index of the greatest (modulus, remainder) pair that is
3051+
* less than or equal to the given (modulus, remainder) pair or -1 if
3052+
* all of them are greater
3053+
*/
3054+
static int
3055+
partition_hash_bsearch(PartitionKey key,
3056+
PartitionBoundInfo boundinfo,
3057+
int modulus, int remainder)
3058+
{
3059+
int lo,
3060+
hi,
3061+
mid;
3062+
3063+
lo = -1;
3064+
hi = boundinfo->ndatums - 1;
3065+
while (lo < hi)
3066+
{
3067+
int32 cmpval,
3068+
bound_modulus,
3069+
bound_remainder;
3070+
3071+
mid = (lo + hi + 1) / 2;
3072+
bound_modulus = DatumGetInt32(boundinfo->datums[mid][0]);
3073+
bound_remainder = DatumGetInt32(boundinfo->datums[mid][1]);
3074+
cmpval = partition_hbound_cmp(bound_modulus, bound_remainder,
3075+
modulus, remainder);
3076+
if (cmpval <= 0)
3077+
{
3078+
lo = mid;
3079+
3080+
if (cmpval == 0)
3081+
break;
3082+
}
3083+
else
3084+
hi = mid - 1;
3085+
}
3086+
3087+
return lo;
3088+
}
3089+
30153090
/*
30163091
* get_default_oid_from_partdesc
30173092
*

0 commit comments

Comments
 (0)