diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c index bb6cddc35e..a38c87bcc1 100644 --- a/src/backend/partitioning/partbounds.c +++ b/src/backend/partitioning/partbounds.c @@ -103,7 +103,8 @@ static PartitionBoundInfo create_list_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping); static PartitionBoundInfo create_range_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping); -static PartitionBoundInfo merge_list_bounds(FmgrInfo *partsupfunc, +static PartitionBoundInfo merge_list_bounds(int partnatts, + FmgrInfo *partsupfunc, Oid *collations, RelOptInfo *outer_rel, RelOptInfo *inner_rel, @@ -222,6 +223,9 @@ static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2); +static int32 partition_lbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation, + int nvalues, Datum *lb_datums, bool *lb_isnulls, + Datum *values, bool *isnulls); static int partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, @@ -1111,7 +1115,8 @@ partition_bounds_merge(int partnatts, return NULL; case PARTITION_STRATEGY_LIST: - return merge_list_bounds(partsupfunc, + return merge_list_bounds(partnatts, + partsupfunc, partcollation, outer_rel, inner_rel, @@ -1155,7 +1160,8 @@ partition_bounds_merge(int partnatts, * join can't handle. */ static PartitionBoundInfo -merge_list_bounds(FmgrInfo *partsupfunc, Oid *partcollation, +merge_list_bounds(int partnatts, + FmgrInfo *partsupfunc, Oid *partcollation, RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinType jointype, List **outer_parts, List **inner_parts) @@ -1221,7 +1227,6 @@ merge_list_bounds(FmgrInfo *partsupfunc, Oid *partcollation, bool *outer_isnull; bool *inner_isnull; bool *merged_isnull = NULL; - bool is_all_match = false; if (outer_bi->isnulls && outer_pos < outer_bi->ndatums) outer_isnull = outer_bi->isnulls[outer_pos]; @@ -1298,14 +1303,15 @@ merge_list_bounds(FmgrInfo *partsupfunc, Oid *partcollation, { Assert(outer_datums != NULL && inner_datums != NULL); //TODO: handle multi-column case - cmpval = partition_lbound_datum_cmp(partsupfunc, partcollation, 1, //TODO: get attr count + cmpval = partition_lbound_datum_cmp(partsupfunc, partcollation, + partnatts, outer_datums, outer_isnull, inner_datums, - inner_isnull, &is_all_match); + inner_isnull); } - if (is_all_match) + if (cmpval == 0) { /* Two list values match exactly. */ Assert(outer_pos < outer_bi->ndatums); @@ -3634,32 +3640,33 @@ partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2) /* * partition_lbound_datum_cmp * - * This function compares the list bound values of all the partition key - * columns. Returns the value of 'cmpval' if the first bound value does - * not match, otherwise returns zero. If it successfully compares the bound - * values for all the partition key, then it sets is_all_match to TRUE. + * Return whether list bound value (given by lb_datums and lb_isnulls) is + * <, =, or > partition key of a tuple (specified in values and isnulls). + * + * nvalues gives the number of values provided in the 'values' and 'isnulls' + * array. partsupfunc and partcollation, both arrays of nvalues elements, + * give the comparison functions and the collations to be used when comparing. */ -int32 +static int32 partition_lbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation, int nvalues, Datum *lb_datums, bool *lb_isnulls, - Datum *values, bool *isnulls, bool *is_all_match) + Datum *values, bool *isnulls) { - int i = 0; - int32 cmpval = 0; + int i; + int32 cmpval; for (i = 0; i < nvalues; i++) { - bool isnull = false; - - if (isnulls) - isnull = isnulls[i]; - - if (lb_isnulls[i] && isnull) - cmpval = 0; - else if (lb_isnulls[i]) - cmpval = 1; - else if (isnull) - cmpval = -1; + /* This always places NULLs after not-NULLs. */ + if (lb_isnulls[i]) + { + if (isnulls && isnulls[i]) + cmpval = 0; /* NULL "=" NULL */ + else + cmpval = 1; /* NULL ">" not-NULL */ + } + else if (isnulls && isnulls[i]) + cmpval = -1; /* not-NULL "<" NULL */ else cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i], partcollation[i], @@ -3669,13 +3676,7 @@ partition_lbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation, break; } - if (i == 0) - return cmpval; - - if (i == nvalues && cmpval == 0) - *is_all_match = true; - - return 0; + return cmpval; } /* @@ -3700,76 +3701,17 @@ partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, while (lo < hi) { int32 cmpval; - bool is_all_match = false; mid = (lo + hi + 1) / 2; cmpval = partition_lbound_datum_cmp(partsupfunc, partcollation, - nvalues, boundinfo->datums[mid], - boundinfo->isnulls[mid], values, - isnulls, &is_all_match); - - if (is_all_match) - { - *is_equal = true; - return mid; - } - - if (cmpval == 0) - { - /* - * Once we find the matching for the first column but if it does not - * match for the any of the other columns, then the binary search - * will not work in all the cases. We should traverse just below - * and above the mid index until we find the match or we reach the - * first mismatch. - */ - int32 off = mid; - while (off >= 1) - { - cmpval = partition_lbound_datum_cmp(partsupfunc, partcollation, - nvalues, boundinfo->datums[off - 1], - boundinfo->isnulls[off - 1], values, - isnulls, &is_all_match); - - if (is_all_match) - { - /* Found the matching bound. Return the offset. */ - *is_equal = true; - return (off - 1); - } - - if (cmpval != 0) - break; - - off--; - } - - off = mid; - while (off < boundinfo->ndatums - 1) - { - cmpval = partition_lbound_datum_cmp(partsupfunc, partcollation, - nvalues, boundinfo->datums[off + 1], - boundinfo->isnulls[off + 1], values, - isnulls, &is_all_match); - - if (is_all_match) - { - /* Found the matching bound. Return the offset. */ - *is_equal = true; - return (off + 1); - } - - if (cmpval != 0) - break; - - off++; - } - } - + nvalues, + boundinfo->datums[mid], + boundinfo->isnulls[mid], + values, isnulls); if (cmpval <= 0) { lo = mid; - *is_equal = (cmpval == 0 && is_all_match); + *is_equal = (cmpval == 0); if (*is_equal) break; } @@ -3935,23 +3877,17 @@ qsort_partition_hbound_cmp(const void *a, const void *b) static int32 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg) { - Datum *val1 = (*(PartitionListValue *const *) a)->values; - Datum *val2 = (*(PartitionListValue *const *) b)->values; - bool *null1 = (*(PartitionListValue *const *) a)->isnulls; - bool *null2 = (*(PartitionListValue *const *) b)->isnulls; - + Datum *vals1 = (*(PartitionListValue *const *) a)->values; + Datum *vals2 = (*(PartitionListValue *const *) b)->values; + bool *isnull1 = (*(PartitionListValue *const *) a)->isnulls; + bool *isnull2 = (*(PartitionListValue *const *) b)->isnulls; PartitionKey key = (PartitionKey) arg; - if (null1[0] && null2[0]) - return 0; - else if (null1[0]) - return 1; - else if (null2[0]) - return -1; - else - return DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0], - key->partcollation[0], - val1[0], val2[0])); + return partition_lbound_datum_cmp(key->partsupfunc, + key->partcollation, + key->partnatts, + vals1, isnull1, + vals2, isnull2); } /* diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h index a4b301bfa1..f46ad3ad23 100644 --- a/src/include/partitioning/partbounds.h +++ b/src/include/partitioning/partbounds.h @@ -24,9 +24,6 @@ struct RelOptInfo; /* avoid including pathnodes.h here */ * descriptor, but may also be used to represent a virtual partitioned * table such as a partitioned joinrel within the planner. * - * A list partition datum that is known to be NULL is never put into the - * datums array. Instead, it is tracked using the null_index field. - * * In the case of range partitioning, ndatums will typically be far less than * 2 * nparts, because a partition's upper bound and the next partition's lower * bound are the same in most common cases, and we only store one of them (the @@ -38,6 +35,10 @@ struct RelOptInfo; /* avoid including pathnodes.h here */ * of datum-tuples with 2 datums, modulus and remainder, corresponding to a * given partition. * + * isnulls is an array of boolean-tuples with key->partnatts booleans values + * each. Currently only used for list partitioning, it stores whether a + * given partition key accepts NULL as value. + * * The datums in datums array are arranged in increasing order as defined by * functions qsort_partition_rbound_cmp(), qsort_partition_list_value_cmp() and * qsort_partition_hbound_cmp() for range, list and hash partitioned tables @@ -126,9 +127,4 @@ extern int partition_range_datum_bsearch(FmgrInfo *partsupfunc, int nvalues, Datum *values, bool *is_equal); extern int partition_hash_bsearch(PartitionBoundInfo boundinfo, int modulus, int remainder); -extern int32 partition_lbound_datum_cmp(FmgrInfo *partsupfunc, - Oid *partcollation, - int nvalues, Datum *lb_datums, - bool *lb_isnulls, Datum *values, - bool *isnulls, bool *is_all_match); #endif /* PARTBOUNDS_H */ diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out index 174f8c2d19..8e704d3928 100644 --- a/src/test/regress/expected/insert.out +++ b/src/test/regress/expected/insert.out @@ -846,20 +846,20 @@ insert into mclparted values (1, null, null); ERROR: no partition of relation "mclparted" found for row DETAIL: Partition key of the failing row contains (a, b, c) = (1, null, null). -- check rows -select tableoid::regclass::text, * from mclparted order by 1; +select tableoid::regclass::text, * from mclparted order by 1, 2, 3, 4; tableoid | a | b | c --------------+---+---+--- mclparted_p1 | 1 | a | 1 + mclparted_p2 | 1 | a | 2 mclparted_p2 | 1 | b | 1 mclparted_p2 | 2 | a | 1 - mclparted_p2 | 1 | a | 2 mclparted_p3 | 3 | c | 3 + mclparted_p3 | 4 | d | 4 mclparted_p3 | 5 | e | 5 mclparted_p3 | 6 | | 6 - mclparted_p3 | 4 | d | 4 - mclparted_p4 | | a | 1 - mclparted_p4 | 1 | | 1 mclparted_p4 | 1 | a | + mclparted_p4 | 1 | | 1 + mclparted_p4 | | a | 1 mclparted_p5 | | | (12 rows) diff --git a/src/test/regress/sql/insert.sql b/src/test/regress/sql/insert.sql index 76e0d004a1..2bfc55c66a 100644 --- a/src/test/regress/sql/insert.sql +++ b/src/test/regress/sql/insert.sql @@ -573,7 +573,7 @@ insert into mclparted values (1, 'a', 10); insert into mclparted values (1, null, null); -- check rows -select tableoid::regclass::text, * from mclparted order by 1; +select tableoid::regclass::text, * from mclparted order by 1, 2, 3, 4; -- cleanup drop table mclparted;