diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c index 1238999c22..4c3a31c82c 100644 --- a/src/backend/partitioning/partbounds.c +++ b/src/backend/partitioning/partbounds.c @@ -72,8 +72,13 @@ typedef struct PartitionRangeBound typedef struct PartitionMap { - int from; - int to; + int nparts; /* number of partitions */ + int *merged_indexes; /* indexes of merged partitions */ + bool *merged; /* flags to indicate whether partitions are + * merged with non-dummy partitions */ + bool did_remapping; /* did we remap partitions? */ + int *old_indexes; /* old indexes of merged partitions if + * did_remapping */ } PartitionMap; static int32 qsort_partition_hbound_cmp(const void *a, const void *b); @@ -115,20 +120,49 @@ static void get_range_key_properties(PartitionKey key, int keynum, Expr **keyCol, Const **lower_val, Const **upper_val); static List *get_range_nulltest(PartitionKey key); -static PartitionBoundInfo partition_range_bounds_merge( +static PartitionBoundInfo partition_range_bounds_merge(int partnatts, FmgrInfo *partsupfuncs, + Oid *partcollations, RelOptInfo *outer_rel, RelOptInfo *inner_rel, - List **outer_parts, List **inner_parts, - JoinType jointype, int partnatts, - FmgrInfo *supfuncs, Oid *collations); + JoinType jointype, + List **outer_parts, List **inner_parts); static PartitionBoundInfo partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *collations, RelOptInfo *outer_rel, RelOptInfo *inner_rel, - List **outer_parts, List **inner_parts, - JoinType jointype); + JoinType jointype, + List **outer_parts, List **inner_parts); +static void init_partition_map(RelOptInfo *rel, PartitionMap *map); +static void free_partition_map(PartitionMap *map); +static int map_and_merge_partitions(PartitionMap *outer_map, PartitionMap *inner_map, + int outer_part, int inner_part, int *next_index); +static int merge_partition_with_dummy(PartitionMap *map, int index, + int *next_index); +static bool process_outer_partition(PartitionMap *outer_map, + PartitionMap *inner_map, + bool outer_has_default, + bool inner_has_default, + int outer_index, + int inner_default, + JoinType jointype, + char strategy, + int *next_index, + int *default_index, + int *merged_index); +static bool process_inner_partition(PartitionMap *outer_map, + PartitionMap *inner_map, + bool outer_has_default, + bool inner_has_default, + int inner_index, + int outer_default, + JoinType jointype, + char strategy, + int *next_index, + int *default_index, + int *merged_index); +static void fix_merged_indexes(PartitionMap *outer_map, PartitionMap *inner_map, + int nmerged, List *merged_indexes); static void generate_matching_part_pairs(RelOptInfo *rel1, RelOptInfo *rel2, - PartitionMap *partmaps1, - PartitionMap *partmaps2, - int nparts1, int nparts2, + PartitionMap *map1, + PartitionMap *map2, int nparts, List **matched_parts1, List **matched_parts2); @@ -136,9 +170,6 @@ static PartitionBoundInfo build_merged_partition_bounds(char strategy, List *merged_datums, List *merged_indexes, List *merged_contents, int null_index, int default_index); -static int map_and_merge_partitions(PartitionMap *outer_maps, - PartitionMap *inner_maps, - int index1, int index2, int *next_index); static int32 partition_range_bound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *collations, PartitionRangeBound *bound1, PartitionRangeBound *bound2); @@ -153,19 +184,21 @@ static bool partition_range_merge_next_lb(int partnatts, FmgrInfo *supfuncs, PartitionRangeDatumKind *next_lb_kind, List **merged_datums, List **merged_kinds, List **merged_indexes); -static bool merge_default_partitions(PartitionBoundInfo outer_bi, - PartitionBoundInfo inner_bi, - PartitionMap *outer_maps, - PartitionMap *inner_maps, +static bool merge_default_partitions(PartitionMap *outer_map, + PartitionMap *inner_map, + bool outer_has_default, + int outer_default, + bool inner_has_default, + int inner_default, JoinType jointype, - int *next_index, int *default_index); -static bool merge_null_partitions(PartitionBoundInfo outer_bi, - PartitionBoundInfo inner_bi, - PartitionMap *outer_maps, - PartitionMap *inner_maps, - JoinType jointype, - int *next_index, int *null_index, - int *default_index); + int *next_index, + int *default_index); +static bool merge_null_partitions(PartitionBoundInfo outer_bi, PartitionBoundInfo inner_bi, + PartitionMap *outer_map, PartitionMap *inner_map, + bool outer_has_default, bool inner_has_default, + bool outer_has_null, bool inner_has_null, + JoinType jointype, int *next_index, + int *default_index, int *null_index); /* * get_qual_from_partbound @@ -3082,8 +3115,8 @@ partition_bounds_merge(int partnatts, int16 *parttyplen, bool *parttypbyval, FmgrInfo *partsupfunc, Oid *partcollation, RelOptInfo *outer_rel, RelOptInfo *inner_rel, - JoinType jointype, - List **outer_parts, List **inner_parts) + JoinType jointype, List **outer_parts, + List **inner_parts) { PartitionBoundInfo merged_bounds; PartitionBoundInfo outer_binfo = outer_rel->boundinfo, @@ -3111,17 +3144,22 @@ partition_bounds_merge(int partnatts, case PARTITION_STRATEGY_LIST: merged_bounds = partition_list_bounds_merge(partsupfunc, partcollation, - outer_rel, inner_rel, - outer_parts, inner_parts, - jointype); + outer_rel, + inner_rel, + jointype, + outer_parts, + inner_parts); break; case PARTITION_STRATEGY_RANGE: - merged_bounds = partition_range_bounds_merge(outer_rel, inner_rel, - outer_parts, inner_parts, - jointype, partnatts, + merged_bounds = partition_range_bounds_merge(partnatts, partsupfunc, - partcollation); + partcollation, + outer_rel, + inner_rel, + jointype, + outer_parts, + inner_parts); break; default: @@ -3336,8 +3374,6 @@ partition_range_merge(int partnatts, FmgrInfo *partsupfuncs, default: elog(ERROR, "unexpected join type %d", jointype); } - - return; } /* @@ -3397,157 +3433,34 @@ partition_range_merge_next_lb(int partnatts, FmgrInfo *partsupfuncs, return true; } -/* - * handle_missing_partition - * - * If a range appears in one of the joining relations but not the other, a row - * in the corresponding partition will not have any join partner in the other - * relation, unless the other relation has a default partition. If a given list - * value is present in one joining relation but not the other, the default - * partition on the other side may contain that value. - * - * In both these cases, such an extra partition forms a joining pair with the - * default partition, if any, on the other side. - * - * If the default partition happens to be on the outer side of the join, the - * resultant partition will act as the default partition of the join relation. - * Otherwise the resultant partition will be associated with the range. - * - * When the default partition is not present in the other relation, the rows in - * the extra partition will be included in the bounds of the join result, if it - * appears on the outer side of the join, since all rows from the outer side - * are included in the join result. - * - * This function handles all these cases. - * - * maps_with_missing and missing_side_default are the partition maps (See - * partition_range/list_bounds_merge() for details) and the index of default - * partition respectively corresponding the side with missing partition. - * - * maps_with_extra and extra_part are the partition maps (See - * partition_range/list_bounds_merge() for details) and the index of extra - * partition respectively corresponding to the side with the extra partition. - * - * It returns true if the matching succeeds, otherwise returns false. - */ -static bool -handle_missing_partition(PartitionMap *maps_with_missing, - PartitionMap *maps_with_extra, - int missing_side_default, - int extra_part, - bool missing_side_outer, - bool missing_side_inner, - int *next_index, int *default_index, - int *merged_index) -{ - bool missing_has_default = (missing_side_default != -1); - - if (missing_has_default) - { - *merged_index = map_and_merge_partitions(maps_with_missing, - maps_with_extra, - missing_side_default, - extra_part, - next_index); - if (*merged_index < 0) - return false; - - if (missing_side_outer) - { - /* - * Default partition on the outer side forms the default - * partition of the join result. - */ - if (*default_index < 0) - *default_index = *merged_index; - else if(*default_index != *merged_index) - { - /* - * Ended up with default partition on the outer side - * being joined with multiple partitions on the inner - * side. We don't support this case. - */ - return false; - } - - /* - * Since the merged partition acts as a default partition, it - * doesn't need a separate index. - */ - *merged_index = -1; - } - } - else if (missing_side_inner) - { - /* - * If this partition has already been mapped (say because we - * found an overlapping range earlier), we know where does it - * fit in the join result. Nothing to do in that case. Else - * create a new merged partition. - */ - PartitionMap *extra_map = &maps_with_extra[extra_part]; - if (extra_map->to < 0) - { - extra_map->to = *next_index; - *next_index = *next_index + 1; - *merged_index = extra_map->to; - } - } - else - *merged_index = -1; - - return true; -} - -static PartitionMap* -init_partition_map(RelOptInfo *rel) -{ - int i, nparts = rel->nparts; - PartitionMap *map; - - map = (PartitionMap *) palloc(sizeof(PartitionMap) * nparts); - - for (i = 0; i < nparts; i++) - { - map[i].from = -1; - map[i].to = -1; - } - - return map; -} - /* * partition_range_bounds_merge * * partition_bounds_merge()'s arm for range partitioned tables. */ static PartitionBoundInfo -partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel, - List **outer_parts, List **inner_parts, - JoinType jointype, int partnatts, - FmgrInfo *partsupfuncs, Oid *partcollations) - +partition_range_bounds_merge(int partnatts, FmgrInfo *partsupfuncs, + Oid *partcollations, + RelOptInfo *outer_rel, RelOptInfo *inner_rel, + JoinType jointype, + List **outer_parts, List **inner_parts) { - PartitionMap *outer_maps = NULL; - PartitionMap *inner_maps = NULL; - int outer_part = 0; - int inner_part = 0; PartitionBoundInfo merged_bounds = NULL; - int outer_lb_index; - int inner_lb_index; - int next_index; + PartitionBoundInfo outer_bi = outer_rel->boundinfo; + PartitionBoundInfo inner_bi = inner_rel->boundinfo; + bool outer_has_default = partition_bound_has_default(outer_bi); + int outer_default = outer_bi->default_index; + bool inner_has_default = partition_bound_has_default(inner_bi); + int inner_default = inner_bi->default_index; + PartitionMap outer_map; + PartitionMap inner_map; + int next_index = 0; int default_index = -1; List *merged_datums = NIL; - List *merged_indexes = NIL; List *merged_kinds = NIL; - PartitionBoundInfo outer_bi = outer_rel->boundinfo, - inner_bi = inner_rel->boundinfo; - int inner_default = inner_bi->default_index; - int outer_default = outer_bi->default_index; - bool inner_has_default = partition_bound_has_default(inner_bi); - bool outer_has_default = partition_bound_has_default(outer_bi); - int outer_nparts = outer_rel->nparts, - inner_nparts = inner_rel->nparts; + List *merged_indexes = NIL; + int outer_lb_index; + int inner_lb_index; Assert(outer_bi->strategy == inner_bi->strategy && outer_bi->strategy == PARTITION_STRATEGY_RANGE); @@ -3555,8 +3468,8 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel, Assert(*outer_parts == NIL); Assert(*inner_parts == NIL); - outer_maps = init_partition_map(outer_rel); - inner_maps = init_partition_map(inner_rel); + init_partition_map(outer_rel, &outer_map); + init_partition_map(inner_rel, &inner_map); /* * Merge the ranges (partitions) from both sides. Every iteration compares @@ -3567,40 +3480,30 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel, * lb_index, for inner or outer side, keeps track of the index of lower bound * datum in PartitionBoundInfo::datums of that side. */ - outer_lb_index = 0; - inner_lb_index = 0; - next_index = 0; + outer_lb_index = inner_lb_index = 0; while (outer_lb_index < outer_bi->ndatums || inner_lb_index < inner_bi->ndatums) { - PartitionRangeBound outer_lb, outer_ub, - inner_lb, inner_ub, - *merged_lb = NULL, - *merged_ub = NULL; - + PartitionRangeBound *merged_lb = NULL; + PartitionRangeBound *merged_ub = NULL; int merged_index = -1; + PartitionRangeBound outer_lb; + PartitionRangeBound outer_ub; + PartitionRangeBound inner_lb; + PartitionRangeBound inner_ub; + int outer_part = -1; + int inner_part = -1; bool overlap; - bool finished_outer = false; - bool finished_inner = false; - - /* Result of bounds comparison per partition_rbound_cmp(). */ - int ub_cmpval; /* Upper bounds comparison result. */ - int lb_cmpval; /* Lower bounds comparison result. */ + int ub_cmpval; + int lb_cmpval; /* Get the range bounds of the next pair of partitions. */ if (outer_lb_index < outer_bi->ndatums) outer_part = partition_get_range_bounds(outer_bi, outer_lb_index, - &outer_lb, &outer_ub); - else - finished_outer = true; - + &outer_lb, &outer_ub); if (inner_lb_index < inner_bi->ndatums) inner_part = partition_get_range_bounds(inner_bi, inner_lb_index, - &inner_lb, &inner_ub); - else - finished_inner = true; - - Assert(!finished_outer || !finished_inner); + &inner_lb, &inner_ub); /* * We run this loop till both the sides finish. This allows to avoid @@ -3612,13 +3515,13 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel, * side. That way we advance the partitions on that side till all of * them are exhausted. */ - if (finished_outer) + if (outer_lb_index >= outer_bi->ndatums) { overlap = false; ub_cmpval = 1; lb_cmpval = 1; } - else if (finished_inner) + else if (inner_lb_index >= inner_bi->ndatums) { overlap = false; ub_cmpval = -1; @@ -3642,7 +3545,7 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel, jointype, &outer_lb, &outer_ub, &inner_lb, &inner_ub, &merged_lb, &merged_ub); - merged_index = map_and_merge_partitions(outer_maps, inner_maps, + merged_index = map_and_merge_partitions(&outer_map, &inner_map, outer_part, inner_part, &next_index); @@ -3681,6 +3584,8 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel, Assert(overlap); /* Move to the next pair of partitions. */ + Assert(outer_lb_index < outer_bi->ndatums); + Assert(inner_lb_index < inner_bi->ndatums); outer_lb_index = partition_range_get_next_lb_index(outer_bi, outer_lb_index); inner_lb_index = partition_range_get_next_lb_index(inner_bi, @@ -3696,37 +3601,31 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel, } else { - /* A range missing from the inner side. */ - bool missing_side_outer; - bool missing_side_inner; + if (inner_has_default || + jointype == JOIN_LEFT || + jointype == JOIN_ANTI || + jointype == JOIN_FULL) + { + if (!process_outer_partition(&outer_map, + &inner_map, + outer_has_default, + inner_has_default, + outer_part, + inner_default, + jointype, + outer_bi->strategy, + &next_index, + &default_index, + &merged_index)) + return NULL; + } merged_lb = &outer_lb; merged_ub = &outer_ub; - - /* - * For a FULL join, inner relation acts as both OUTER and INNER - * relation. For LEFT and ANTI join the inner relation acts as - * INNER relation. For INNER and SEMI join OUTER and INNER - * differentiation is immaterial. - */ - missing_side_inner = (jointype == JOIN_FULL || - jointype == JOIN_LEFT || - jointype == JOIN_ANTI); - missing_side_outer = (jointype == JOIN_FULL); - if (!handle_missing_partition(inner_maps, - outer_maps, - inner_default, - outer_part, - missing_side_outer, - missing_side_inner, - &next_index, - &default_index, - &merged_index)) - return NULL; } /* Move to the next partition on the outer side. */ - Assert(!finished_outer); + Assert(outer_lb_index < outer_bi->ndatums); outer_lb_index = partition_range_get_next_lb_index(outer_bi, outer_lb_index); } @@ -3741,38 +3640,28 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel, } else { - /* A range missing from the outer side. */ - bool missing_side_outer; - bool missing_side_inner; + if (outer_has_default || jointype == JOIN_FULL) + { + if (!process_inner_partition(&outer_map, + &inner_map, + outer_has_default, + inner_has_default, + inner_part, + outer_default, + jointype, + outer_bi->strategy, + &next_index, + &default_index, + &merged_index)) + return NULL; + } merged_lb = &inner_lb; merged_ub = &inner_ub; - - /* - * For a FULL join, outer relation acts as both OUTER and INNER - * relation. For LEFT and ANTI join the outer relation acts as - * OUTER relation. For INNER and SEMI join OUTER and INNER - * differentiation is immaterial. - */ - missing_side_outer = (jointype == JOIN_FULL || - jointype == JOIN_LEFT || - jointype == JOIN_ANTI); - missing_side_inner = (jointype == JOIN_FULL); - - if (!handle_missing_partition(outer_maps, - inner_maps, - outer_default, - inner_part, - missing_side_outer, - missing_side_inner, - &next_index, - &default_index, - &merged_index)) - return NULL; } /* Move to the next partition on the inner side. */ - Assert (!finished_inner); + Assert(inner_lb_index < inner_bi->ndatums); inner_lb_index = partition_range_get_next_lb_index(inner_bi, inner_lb_index); } @@ -3803,16 +3692,28 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel, merged_indexes = lappend_int(merged_indexes, merged_index); } - if (!merge_default_partitions(outer_bi, inner_bi, - outer_maps, inner_maps, - jointype, &next_index, - &default_index)) - return NULL; + /* Merge default partitions if any. */ + if (outer_has_default || inner_has_default) + { + if (!merge_default_partitions(&outer_map, + &inner_map, + outer_has_default, + outer_default, + inner_has_default, + inner_default, + jointype, + &next_index, + &default_index)) + return NULL; + } + else + Assert(default_index == -1); + + Assert(!outer_map.did_remapping && !inner_map.did_remapping); /* Use maps to match partition from the joining relations. */ generate_matching_part_pairs(outer_rel, inner_rel, - outer_maps, inner_maps, - outer_nparts, inner_nparts, + &outer_map, &inner_map, next_index, outer_parts, inner_parts); @@ -3829,6 +3730,8 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel, } /* Free any memory we used in this function. */ + free_partition_map(&outer_map); + free_partition_map(&inner_map); list_free(merged_datums); list_free(merged_indexes); list_free(merged_kinds); @@ -3845,27 +3748,27 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel, static PartitionBoundInfo partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation, RelOptInfo *outer_rel, RelOptInfo *inner_rel, - List **outer_parts, List **inner_parts, - JoinType jointype) + JoinType jointype, + List **outer_parts, List **inner_parts) { - PartitionMap *outer_maps = NULL; - PartitionMap *inner_maps = NULL; - int cnto; - int cnti; - List *merged_datums = NIL; - List *merged_indexes = NIL; + PartitionBoundInfo merged_bounds = NULL; + PartitionBoundInfo outer_bi = outer_rel->boundinfo; + PartitionBoundInfo inner_bi = inner_rel->boundinfo; + bool outer_has_default = partition_bound_has_default(outer_bi); + int outer_default = outer_bi->default_index; + bool inner_has_default = partition_bound_has_default(inner_bi); + int inner_default = inner_bi->default_index; + bool outer_has_null = partition_bound_accepts_nulls(outer_bi); + bool inner_has_null = partition_bound_accepts_nulls(inner_bi); + PartitionMap outer_map; + PartitionMap inner_map; int next_index = 0; int null_index = -1; int default_index = -1; - PartitionBoundInfo merged_bounds = NULL; - PartitionBoundInfo outer_bi = outer_rel->boundinfo, - inner_bi = inner_rel->boundinfo; - int *outer_indexes = outer_bi->indexes; - int *inner_indexes = inner_bi->indexes; - int outer_default = outer_bi->default_index; - int inner_default = inner_bi->default_index; - int outer_nparts = outer_rel->nparts, - inner_nparts = inner_rel->nparts; + List *merged_datums = NIL; + List *merged_indexes = NIL; + int cnto; + int cnti; Assert(*outer_parts == NIL); Assert(*inner_parts == NIL); @@ -3876,8 +3779,8 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation, /* List partitions do not require unbounded ranges. */ Assert(!outer_bi->kind && !inner_bi->kind); - outer_maps = init_partition_map(outer_rel); - inner_maps = init_partition_map(inner_rel); + init_partition_map(outer_rel, &outer_map); + init_partition_map(inner_rel, &inner_map); /* * Merge the list value datums from both sides. Every iteration compares a @@ -3889,15 +3792,15 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation, cnto = cnti = 0; while (cnto < outer_bi->ndatums || cnti < inner_bi->ndatums) { + Datum *merged_datum = NULL; + int merged_index = -1; Datum *odatums; Datum *idatums; - int o_index; - int i_index; int cmpval; - int merged_index = -1; - Datum *merged_datum; - bool finished_inner; - bool finished_outer; + + /* Get the list datums of the next pair of partitions. */ + odatums = cnto < outer_bi->ndatums ? outer_bi->datums[cnto] : NULL; + idatums = cnti < inner_bi->ndatums ? inner_bi->datums[cnti] : NULL; /* * We run this loop till both the sides finish. This allows to avoid @@ -3909,51 +3812,23 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation, * its datums are exhausted. */ if (cnto >= outer_bi->ndatums) - { - finished_outer = true; - odatums = NULL; - o_index = -1; - } - else - { - finished_outer = false; - odatums = outer_bi->datums[cnto]; - o_index = outer_indexes[cnto]; - } - - if (cnti >= inner_bi->ndatums) - { - finished_inner = true; - idatums = NULL; - i_index = -1; - } - else - { - finished_inner = false; - idatums = inner_bi->datums[cnti]; - i_index = inner_indexes[cnti]; - } - - /* If we exhausted both the sides, we won't enter the loop. */ - Assert(!finished_inner || !finished_outer); - - if (finished_outer) cmpval = 1; - else if (finished_inner) + else if (cnti >= inner_bi->ndatums) cmpval = -1; else { - /* Every list datum should map to a valid partition index. */ - Assert(o_index >= 0 && i_index >= 0 && - odatums != NULL && idatums != NULL); - + Assert(odatums != NULL && idatums != NULL); cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0], partcollation[0], - odatums[0], idatums[0])); + odatums[0], + idatums[0])); } if (cmpval == 0) { + int o_index = outer_bi->indexes[cnto]; + int i_index = inner_bi->indexes[cnti]; + /* * Datums match. Rows on either side with these datums as partition * key value will join and will be part of the partition of the @@ -3963,7 +3838,8 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation, * partition containing it. */ merged_datum = odatums; - merged_index = map_and_merge_partitions(outer_maps, inner_maps, + Assert(o_index >= 0 && i_index >= 0); + merged_index = map_and_merge_partitions(&outer_map, &inner_map, o_index, i_index, &next_index); @@ -3976,74 +3852,64 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation, } else if (cmpval < 0) { - bool missing_side_outer; - bool missing_side_inner; + Assert(cnto < outer_bi->ndatums); /* A datum missing from the inner side. */ - merged_index = -1; merged_datum = odatums; - /* - * For a FULL join, inner relation acts as both OUTER and INNER - * relation. For LEFT and ANTI join the inner relation acts as - * INNER relation. For INNER and SEMI join OUTER and INNER - * differentiation is immaterial. - */ - missing_side_inner = (jointype == JOIN_FULL || - jointype == JOIN_LEFT || - jointype == JOIN_ANTI); - missing_side_outer = (jointype == JOIN_FULL); - - if (!handle_missing_partition(inner_maps, - outer_maps, - inner_default, - o_index, - missing_side_outer, - missing_side_inner, - &next_index, - &default_index, - &merged_index)) - return NULL; + if (inner_has_default || + jointype == JOIN_LEFT || + jointype == JOIN_ANTI || + jointype == JOIN_FULL) + { + int o_index = outer_bi->indexes[cnto]; + + Assert(o_index >= 0); + if (!process_outer_partition(&outer_map, + &inner_map, + outer_has_default, + inner_has_default, + o_index, + inner_default, + jointype, + outer_bi->strategy, + &next_index, + &default_index, + &merged_index)) + return NULL; + } /* Move to the next datum on the outer side. */ - Assert(!finished_outer); cnto++; } else { - bool missing_side_outer; - bool missing_side_inner; - Assert(cmpval > 0); + Assert(cnti < inner_bi->ndatums); /* A datum missing from the outer side. */ - merged_index = -1; merged_datum = idatums; - /* - * For a FULL join, outer relation acts as both OUTER and INNER - * relation. For LEFT and ANTI join the outer relation acts as - * OUTER relation. For INNER and SEMI join OUTER and INNER - * differentiation is immaterial. - */ - missing_side_outer = (jointype == JOIN_FULL || - jointype == JOIN_LEFT || - jointype == JOIN_ANTI); - missing_side_inner = (jointype == JOIN_FULL); - - if (!handle_missing_partition(outer_maps, - inner_maps, - outer_default, - i_index, - missing_side_outer, - missing_side_inner, - &next_index, - &default_index, - &merged_index)) - return NULL; + if (outer_has_default || jointype == JOIN_FULL) + { + int i_index = inner_bi->indexes[cnti]; + + Assert(i_index >= 0); + if (!process_inner_partition(&outer_map, + &inner_map, + outer_has_default, + inner_has_default, + i_index, + outer_default, + jointype, + outer_bi->strategy, + &next_index, + &default_index, + &merged_index)) + return NULL; + } - /* Move to the next datum on the right side. */ - Assert(!finished_inner); + /* Move to the next datum on the inner side. */ cnti++; } @@ -4058,22 +3924,43 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation, } } - if (!merge_null_partitions(outer_bi, inner_bi, - outer_maps, inner_maps, - jointype, &next_index, &null_index, - &default_index)) - return NULL; + /* Merge null partitions if any. */ + if (outer_has_null || inner_has_null) + { + if (!merge_null_partitions(outer_bi, inner_bi, + &outer_map, &inner_map, + outer_has_default, inner_has_default, + outer_has_null, inner_has_null, + jointype, &next_index, &default_index, + &null_index)) + return NULL; + } + else + Assert(null_index == -1); - if (!merge_default_partitions(outer_bi, inner_bi, - outer_maps, inner_maps, - jointype, &next_index, - &default_index)) - return NULL; + /* Merge default partitions if any. */ + if (outer_has_default || inner_has_default) + { + if (!merge_default_partitions(&outer_map, &inner_map, + outer_has_default, outer_default, + inner_has_default, inner_default, + jointype, &next_index, &default_index)) + return NULL; + } + else + Assert(default_index == -1); + + /* Fix the merged_indexes list if necessary. */ + if (outer_map.did_remapping || inner_map.did_remapping) + { + Assert(jointype == JOIN_FULL); + fix_merged_indexes(&outer_map, &inner_map, + next_index, merged_indexes); + } /* Use maps to match partition from the joining relations. */ generate_matching_part_pairs(outer_rel, inner_rel, - outer_maps, inner_maps, - outer_nparts, inner_nparts, + &outer_map, &inner_map, next_index, outer_parts, inner_parts); @@ -4081,7 +3968,7 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation, if (*outer_parts && *inner_parts) { Assert(list_length(*outer_parts) == list_length(*inner_parts)); - Assert(list_length(*outer_parts) == next_index); + Assert(list_length(*outer_parts) <= next_index); merged_bounds = build_merged_partition_bounds(outer_bi->strategy, merged_datums, merged_indexes, NIL, @@ -4089,6 +3976,8 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation, } /* Free up all extra memory before returning from this function. */ + free_partition_map(&outer_map); + free_partition_map(&inner_map); list_free(merged_datums); list_free(merged_indexes); @@ -4096,132 +3985,400 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation, } /* - * map_and_merge_partitions - * - * If the two given partitions (given by index1 and index2 resp.) are - * already mapped to each other return the index of corresponding partition in - * the merged set of partitions. If they do not have a merged partition - * associated with them, assign a new merged partition index. If the - * partitions are already mapped and their mapped partitions are different from - * each other, they can not be merged, so return -1. - * - * partmaps1[i] gives the mapping of partitions for both relations. It - * describes which partition of relation 2 matches ith partition of relation 1, - * and which partition in the merged set matches ith partition of relation 1 - * maps to. Similarly for partmap2. + * init_partition_map * - * index1 and index2 are the indexes of matching partition from respective - * relations. - * - * *next_index is used and incremented when the given partitions require a new - * merged partition. + * Initialize a PartitionMap struct for given relation. */ - -static int -map_and_merge_partitions(PartitionMap *partmaps1, PartitionMap *partmaps2, - int index1, int index2, int *next_index) +static void +init_partition_map(RelOptInfo *rel, PartitionMap *map) { - PartitionMap *partmap1 = &partmaps1[index1]; - PartitionMap *partmap2 = &partmaps2[index2]; - int merged_index; + int nparts = rel->nparts; + int i; - /* - * If both the partitions are not mapped to each other, update the - * maps. - */ - if (partmap1->from < 0 && partmap2->from < 0) + map->nparts = nparts; + map->merged_indexes = (int *) palloc(sizeof(int) * nparts); + map->merged = (bool *) palloc(sizeof(bool) * nparts); + map->did_remapping = false; + map->old_indexes = (int *) palloc(sizeof(int) * nparts); + for (i = 0; i < nparts; i++) { - partmap1->from = index2; - partmap2->from = index1; + map->merged_indexes[i] = map->old_indexes[i] = -1; + map->merged[i] = false; } +} + +/* + * free_partition_map + */ +static void +free_partition_map(PartitionMap *map) +{ + pfree(map->merged_indexes); + pfree(map->merged); + pfree(map->old_indexes); +} + +/* + * map_and_merge_partitions + * + * *next_index is incremented when creating a new merged partition associated + * with the given partitions. + */ +static int +map_and_merge_partitions(PartitionMap *outer_map, PartitionMap *inner_map, + int outer_index, int inner_index, int *next_index) +{ + int outer_merged_index; + bool outer_merged; + int inner_merged_index; + bool inner_merged; + + Assert(outer_index >= 0 && outer_index < outer_map->nparts); + outer_merged_index = outer_map->merged_indexes[outer_index]; + outer_merged = outer_map->merged[outer_index]; + Assert(inner_index >= 0 && inner_index < inner_map->nparts); + inner_merged_index = inner_map->merged_indexes[inner_index]; + inner_merged = inner_map->merged[inner_index]; /* - * If the given to partitions map to each other, find the corresponding - * merged partition index . + * Handle cases where both partitions are mapped to merged partitions. */ - if (partmap1->from == index2 && partmap2->from == index1) + if (outer_merged_index >= 0 && inner_merged_index >= 0) { /* - * If both the partitions are mapped to the same merged partition, get - * the index of merged partition. + * If the mereged partitions are the same, no need to do anything; + * return the index of the merged partition. Otherwise, if both + * partitions are merged with dummy partitions, re-merge them; map + * them to the merged partition with the smaller of the two merged + * indexes and return the smaller index. Otherwise they can't be + * merged, so return -1. */ - if (partmap1->to == partmap2->to) + if (outer_merged_index == inner_merged_index) + { + Assert(outer_merged); + Assert(inner_merged); + return outer_merged_index; + } + if (!outer_merged && !inner_merged) { - merged_index = partmap1->to; - /* - * If the given two partitions do not have a merged partition - * associated with them, allocate a new merged partition. + * Note that we will fix the larger index that have been added to + * the merged_indexes list so far in fix_merged_indexes(). */ - if (merged_index < 0) + if (outer_merged_index < inner_merged_index) { - merged_index = *next_index; - *next_index = *next_index + 1; - partmap1->to = merged_index; - partmap2->to = merged_index; + outer_map->merged[outer_index] = true; + inner_map->merged_indexes[inner_index] = outer_merged_index; + inner_map->merged[inner_index] = true; + inner_map->did_remapping = true; + inner_map->old_indexes[inner_index] = inner_merged_index; + return outer_merged_index; + } + else + { + inner_map->merged[inner_index] = true; + outer_map->merged_indexes[outer_index] = inner_merged_index; + outer_map->merged[outer_index] = true; + outer_map->did_remapping = true; + outer_map->old_indexes[outer_index] = outer_merged_index; + return inner_merged_index; } } + return -1; + } + + /* At least one partition isn't mapped to a merged partition. */ + Assert(outer_merged_index == -1 || inner_merged_index == -1); + /* + * If neither of partitions isn't mapped, assign them a new merged + * partition and return the index of the merged partition. Otherwise, if + * one of partitions is merged with a dummy relation (and the other isn't + * merged), re-merge it with the other, with the same index, and return + * the index. Otherwise they can't be merged, so return -1. + */ + if (outer_merged_index == -1 && inner_merged_index == -1) + { + int merged_index = *next_index; + + Assert(!outer_merged); + Assert(!inner_merged); + outer_map->merged_indexes[outer_index] = merged_index; + outer_map->merged[outer_index] = true; + inner_map->merged_indexes[inner_index] = merged_index; + inner_map->merged[inner_index] = true; + *next_index = *next_index + 1; + return merged_index; + } + if (outer_merged_index >= 0 && !outer_map->merged[outer_index]) + { + Assert(inner_merged_index == -1); + Assert(!inner_merged); + inner_map->merged_indexes[inner_index] = outer_merged_index; + inner_map->merged[inner_index] = true; + outer_map->merged[outer_index] = true; + return outer_merged_index; + } + if (inner_merged_index >= 0 && !inner_map->merged[inner_index]) + { + Assert(outer_merged_index == -1); + Assert(!outer_merged); + outer_map->merged_indexes[outer_index] = inner_merged_index; + outer_map->merged[outer_index] = true; + inner_map->merged[inner_index] = true; + return inner_merged_index; + } + return -1; +} + +/* + * merge_partition_with_dummy + * + * *next_index is incremented. + */ +static int +merge_partition_with_dummy(PartitionMap *map, int index, int *next_index) +{ + int merged_index = *next_index; + + Assert(index >= 0 && index < map->nparts); + Assert(map->merged_indexes[index] == -1); + Assert(!map->merged[index]); + map->merged_indexes[index] = merged_index; + /* Leave the merged flag alone! */ + *next_index = *next_index + 1; + return merged_index; +} + +/* + * process_outer_partition + * + * Determine the merged partition associated with the given outer partition. + * + * *next_index is incremented when creating a new merged partition associated + * with the given outer partition. + */ +static bool +process_outer_partition(PartitionMap *outer_map, + PartitionMap *inner_map, + bool outer_has_default, + bool inner_has_default, + int outer_index, + int inner_default, + JoinType jointype, + char strategy, + int *next_index, + int *default_index, + int *merged_index) +{ + /* + * If the inner side has the default partition, the outer partition has to + * be joined with the default partition; try merging them. Otherwise, we + * should in an outer join, in which case the outer partition has to be + * scanned all the way anyway; if the outer partition is already mapped to + * a merged partition, get it, otherwise create a new merged partition by + * merging the outer partition with a dummy partition. + */ + if (inner_has_default) + { /* - * If partition from one relation was mapped to a merged partition but - * not the partition from the other relation, map the same merged - * partition to the partition from other relation, since matching - * partitions map to the same merged partition. + * If the outer side has the default partition as well, we need to + * merge the default partitions (see merge_default_partitions()); give + * up on it. */ - else if (partmap1->to >= 0 && partmap2->to < 0) + if (outer_has_default) + return false; + + *merged_index = map_and_merge_partitions(outer_map, inner_map, + outer_index, inner_default, + next_index); + if (*merged_index == -1) + return false; + + /* + * If this is a FULL join, the merged partition would act as the + * default partition of the join; record the index in *default_index + * if not done yet. + */ + if (jointype == JOIN_FULL) { - partmap2->to = partmap1->to; - merged_index = partmap1->to; + if (*default_index == -1) + *default_index = *merged_index; + else + Assert(*merged_index == *default_index); + /* Don't add this index to the list of merged indexes. */ + *merged_index = -1; } - else if (partmap1->to < 0 && partmap2->to >= 0) + } + else + { + Assert(jointype == JOIN_LEFT || jointype == JOIN_ANTI || + jointype == JOIN_FULL); + + /* + * In range partitioning, if the given outer partition is already + * merged (eg, because we found an overlapping range earlier), we know + * where it fits in the join result; nothing to do in that case. Else + * create a new merged partition. + */ + if (outer_map->merged_indexes[outer_index] >= 0) { - partmap1->to = partmap2->to; - merged_index = partmap2->to; + if (strategy == PARTITION_STRATEGY_LIST) + *merged_index = outer_map->merged_indexes[outer_index]; + else + { + Assert(strategy == PARTITION_STRATEGY_RANGE); + *merged_index = -1; + } } else - { - Assert(partmap1->to != partmap2->to && - partmap1->to >= 0 && partmap2->to >= 0); + *merged_index = merge_partition_with_dummy(outer_map, outer_index, + next_index); + } + return true; +} - /* - * Both the partitions map to different merged partitions. This - * means that multiple partitions from one relation matches to one - * partition from the other relation. Partition-wise join does not - * handle this case right now, since it requires ganging multiple - * partitions together (into one RelOptInfo). - */ - merged_index = -1; +/* + * process_inner_partition + * + * Determine the merged partition associated with the given inner partition. + * + * *next_index is incremented when creating a new merged partition associated + * with the given outer partition. + */ +static bool +process_inner_partition(PartitionMap *outer_map, + PartitionMap *inner_map, + bool outer_has_default, + bool inner_has_default, + int inner_index, + int outer_default, + JoinType jointype, + char strategy, + int *next_index, + int *default_index, + int *merged_index) +{ + /* + * If the outer side has the default partition, the inner partition has to + * be joined with the default partition; try merging them. Otherwise, we + * should in an FULL join, in which case the inner partition has to be + * scanned all the way anyway; if the inner partition is already mapped to + * a merged partition, get it, otherwise create a new merged partition by + * merging the inner partition with a dummy partition. + */ + if (outer_has_default) + { + /* + * If the inner side has the default partition as well, we need to + * merge the default partitions (see merge_default_partitions()); give + * up on it. + */ + if (inner_has_default) + return false; + + *merged_index = map_and_merge_partitions(outer_map, inner_map, + outer_default, inner_index, + next_index); + if (*merged_index == -1) + return false; + + /* + * If this is an outer join, the merged partition would act as the + * default partition of the join; record the index in *default_index + * if not done yet. + */ + if (jointype == JOIN_LEFT || jointype == JOIN_ANTI || + jointype == JOIN_FULL) + { + if (*default_index == -1) + *default_index = *merged_index; + else + Assert(*merged_index == *default_index); + /* Don't add this index to the list of merged indexes. */ + *merged_index = -1; } } else { + Assert(jointype == JOIN_FULL); + /* - * Multiple partitions from one relation map to one partition from the - * other relation. Partition-wise join does not handle this case right - * now, since it requires ganging multiple partitions together (into - * one RelOptInfo). + * In range partitioning, if the given outer partition is already + * merged (eg, because we found an overlapping range earlier), we know + * where it fits in the join result; nothing to do in that case. Else + * create a new merged partition. */ - merged_index = -1; + if (inner_map->merged_indexes[inner_index] >= 0) + { + if (strategy == PARTITION_STRATEGY_LIST) + *merged_index = inner_map->merged_indexes[inner_index]; + else + { + Assert(strategy == PARTITION_STRATEGY_RANGE); + *merged_index = -1; + } + } + else + *merged_index = merge_partition_with_dummy(inner_map, inner_index, + next_index); } + return true; +} - return merged_index; +/* + * fix_merged_indexes + */ +static void +fix_merged_indexes(PartitionMap *outer_map, PartitionMap *inner_map, + int nmerged, List *merged_indexes) +{ + int *new_indexes; + int merged_index; + int i; + ListCell *lc; + + new_indexes = (int *) palloc(sizeof(int) * nmerged); + for (i = 0; i < nmerged; i++) + new_indexes[i] = -1; + + /* Build the mapping of old merged indexes to new merged indexes. */ + if (outer_map->did_remapping) + { + for (i = 0; i < outer_map->nparts; i++) + { + merged_index = outer_map->old_indexes[i]; + if (merged_index >= 0) + new_indexes[merged_index] = outer_map->merged_indexes[i]; + } + } + if (inner_map->did_remapping) + { + for (i = 0; i < inner_map->nparts; i++) + { + merged_index = inner_map->old_indexes[i]; + if (merged_index >= 0) + new_indexes[merged_index] = inner_map->merged_indexes[i]; + } + } + + /* Fix the merged_indexes list using the mapping. */ + foreach(lc, merged_indexes) + { + merged_index = lfirst_int(lc); + Assert(merged_index >= 0); + if (new_indexes[merged_index] >= 0) + lfirst_int(lc) = new_indexes[merged_index]; + } + + pfree(new_indexes); } /* * generate_matching_part_pairs * - * partmaps1 map each partition from either side of the join to a merged - * partition resp. E.g. partmaps1[i].to gives the merged partition to which ith - * partition of first relation maps. Similarly for partmap2. If - * partmaps1[i].to == partmaps2[j].to, i and j form the matching pair of - * partitions. - * - * Given these maps this function produces the list pairs of partitions which - * when joined produce the merged partitions in the order of merged partition - * indexes. - * - * nparts1 and nparts2 are the number of partitions of the joining relations - * resp. + * This function produces the list pairs of partitions which produce merged + * partitions in the order of merged partition indexes. * * nparts is the number of merged partitions. * @@ -4231,15 +4388,20 @@ map_and_merge_partitions(PartitionMap *partmaps1, PartitionMap *partmaps2, */ static void generate_matching_part_pairs(RelOptInfo *rel1, RelOptInfo *rel2, - PartitionMap *partmaps1, PartitionMap *partmaps2, - int nparts1, int nparts2, int nparts, + PartitionMap *map1, PartitionMap *map2, + int nparts, List **matched_parts1, List **matched_parts2) { + int nparts1 = map1->nparts; + int nparts2 = map2->nparts; int *matching1, *matching2; int i; int max_nparts; + Assert(map1->nparts == rel1->nparts); + Assert(map2->nparts == rel2->nparts); + *matched_parts1 = NIL; *matched_parts2 = NIL; @@ -4254,23 +4416,22 @@ generate_matching_part_pairs(RelOptInfo *rel1, RelOptInfo *rel2, { if (i < nparts1) { - PartitionMap outer_map = partmaps1[i]; + int merged_index = map1->merged_indexes[i]; - if (outer_map.to >= 0) + if (merged_index >= 0) { - Assert(outer_map.to < nparts); - matching1[outer_map.to] = i; + Assert(merged_index < nparts); + matching1[merged_index] = i; } } - if (i < nparts2) { - PartitionMap inner_map = partmaps2[i]; + int merged_index = map2->merged_indexes[i]; - if (inner_map.to >= 0) + if (merged_index >= 0) { - Assert(inner_map.to < nparts); - matching2[inner_map.to] = i; + Assert(merged_index < nparts); + matching2[merged_index] = i; } } } @@ -4280,8 +4441,8 @@ generate_matching_part_pairs(RelOptInfo *rel1, RelOptInfo *rel2, int part1 = matching1[i]; int part2 = matching2[i]; - /* At least one of the partitions should exist. */ - Assert(part1 >= 0 || part2 >= 0); + if (part1 == -1 && part2 == -1) + continue; *matched_parts1 = lappend(*matched_parts1, part1 >= 0 ? rel1->part_rels[part1] : NULL); @@ -4357,57 +4518,53 @@ build_merged_partition_bounds(char strategy, List *merged_datums, * appear in the join result, so create a default merged partition. */ static bool -merge_default_partitions(PartitionBoundInfo outer_bi, PartitionBoundInfo inner_bi, - PartitionMap *outer_maps, PartitionMap *inner_maps, - JoinType jointype, int *next_index, int *default_index) +merge_default_partitions(PartitionMap *outer_map, PartitionMap *inner_map, + bool outer_has_default, int outer_default, + bool inner_has_default, int inner_default, + JoinType jointype, int *next_index, + int *default_index) { - int outer_default = outer_bi->default_index; - int inner_default = inner_bi->default_index; - bool outer_has_default = partition_bound_has_default(outer_bi); - bool inner_has_default = partition_bound_has_default(inner_bi); - bool merged = true; - PartitionMap *outer_default_map = NULL; - PartitionMap *inner_default_map = NULL; + Assert(outer_has_default || inner_has_default); - if (outer_has_default) - outer_default_map = &outer_maps[outer_default]; - - if (inner_has_default) - inner_default_map = &inner_maps[inner_default]; - - if (!outer_has_default && !inner_has_default) - Assert(*default_index < 0); - else if (outer_default_map != NULL && inner_default_map == NULL) + if (outer_has_default && !inner_has_default) { if (jointype == JOIN_LEFT || jointype == JOIN_FULL || jointype == JOIN_ANTI) { - if (outer_default_map->to < 0) + int merged_index; + + Assert(outer_default >= 0 && outer_default < outer_map->nparts); + merged_index = outer_map->merged_indexes[outer_default]; + if (merged_index == -1) { - outer_default_map->to = *next_index; - *next_index = *next_index + 1; - Assert(*default_index < 0); - *default_index = outer_default_map->to; + Assert(*default_index == -1); + *default_index = merge_partition_with_dummy(outer_map, + outer_default, + next_index); } else - Assert(*default_index == outer_default_map->to); + Assert(*default_index == merged_index); } else Assert(*default_index < 0); } - else if (outer_default_map == NULL && inner_default_map != NULL) + else if (!outer_has_default && inner_has_default) { if (jointype == JOIN_FULL) { - if (inner_default_map->to < 0) + int merged_index; + + Assert(inner_default >= 0 && inner_default < inner_map->nparts); + merged_index = inner_map->merged_indexes[inner_default]; + if (merged_index == -1) { - inner_default_map->to = *next_index; - *next_index = *next_index + 1; - Assert(*default_index < 0); - *default_index = inner_default_map->to; + Assert(*default_index == -1); + *default_index = merge_partition_with_dummy(inner_map, + inner_default, + next_index); } else - Assert(*default_index == inner_default_map->to); + Assert(*default_index == merged_index); } else Assert(*default_index < 0); @@ -4416,15 +4573,16 @@ merge_default_partitions(PartitionBoundInfo outer_bi, PartitionBoundInfo inner_b { Assert(outer_has_default && inner_has_default); - *default_index = map_and_merge_partitions(outer_maps, inner_maps, - outer_default, inner_default, + *default_index = map_and_merge_partitions(outer_map, + inner_map, + outer_default, + inner_default, next_index); - - if (*default_index < 0) - merged = false; + if (*default_index == -1) + return false; } - return merged; + return true; } /* @@ -4444,101 +4602,83 @@ merge_default_partitions(PartitionBoundInfo outer_bi, PartitionBoundInfo inner_b */ static bool merge_null_partitions(PartitionBoundInfo outer_bi, PartitionBoundInfo inner_bi, - PartitionMap *outer_maps, PartitionMap *inner_maps, + PartitionMap *outer_map, PartitionMap *inner_map, + bool outer_has_default, bool inner_has_default, + bool outer_has_null, bool inner_has_null, JoinType jointype, int *next_index, - int *null_index, int *default_index) + int *default_index, int *null_index) { - bool outer_has_null = partition_bound_accepts_nulls(outer_bi); - bool inner_has_null = partition_bound_accepts_nulls(inner_bi); - int outer_ni = outer_bi->null_index; - int inner_ni = inner_bi->null_index; - int outer_default = outer_bi->default_index; - int inner_default = inner_bi->default_index; - bool merged = true; + Assert(outer_has_null || inner_has_null); + Assert(*null_index == -1); - if (!outer_has_null && !inner_has_null) - Assert(*null_index < 0); - else if (outer_has_null && !inner_has_null) + if (outer_has_null && !inner_has_null) { int merged_index = -1; - bool missing_side_outer; - bool missing_side_inner; - - /* - * For a FULL join, inner relation acts as both OUTER and INNER - * relation. For LEFT and ANTI join the inner relation acts as - * INNER relation. For INNER and SEMI join OUTER and INNER - * differentiation is immaterial. - */ - missing_side_inner = (jointype == JOIN_FULL || - jointype == JOIN_LEFT || - jointype == JOIN_ANTI); - missing_side_outer = (jointype == JOIN_FULL); - - merged = handle_missing_partition(inner_maps, - outer_maps, - inner_default, - outer_ni, - missing_side_outer, - missing_side_inner, next_index, - default_index, &merged_index); - *null_index = merged_index; /* * If the NULL partition was missing from the inner side of the join, - * the partition of the join to which the outer null partition maps - * will contain the NULL values and thus becomes the NULL partition of + * the partition of the join to which the NULL partition matches will + * contain the NULL values and thus become the NULL partition of the * the join. */ - if (missing_side_inner) - *null_index = outer_maps[outer_ni].to; + if (inner_has_default || + jointype == JOIN_LEFT || + jointype == JOIN_ANTI || + jointype == JOIN_FULL) + { + if (!process_outer_partition(outer_map, + inner_map, + outer_has_default, + inner_has_default, + outer_bi->null_index, + inner_bi->default_index, + jointype, + outer_bi->strategy, + next_index, + default_index, + &merged_index)) + return false; + } + *null_index = merged_index; } else if (!outer_has_null && inner_has_null) { int merged_index = -1; - bool missing_side_outer; - bool missing_side_inner; - - /* - * For a FULL join, outer relation acts as both OUTER and INNER - * relation. For LEFT and ANTI join the outer relation acts as OUTER - * relation. For INNER and SEMI join OUTER and INNER differentiation is - * immaterial. - */ - missing_side_outer = (jointype == JOIN_FULL || - jointype == JOIN_LEFT || - jointype == JOIN_ANTI); - missing_side_inner = (jointype == JOIN_FULL); - merged = handle_missing_partition(outer_maps, - inner_maps, - outer_default, - inner_ni, - missing_side_outer, - missing_side_inner, - next_index, default_index, - &merged_index); - *null_index = merged_index; /* * If the NULL partition was missing from the inner side of the join, - * the partition of the join, to which the outer side null partition maps, - * will contain the NULL values and thus becomes the NULL partition of + * the partition of the join to which the NULL partition matches will + * contain the NULL values and thus become the NULL partition of the * the join. */ - if (missing_side_inner) - *null_index = inner_maps[inner_ni].to; + if (outer_has_default || jointype == JOIN_FULL) + { + if (!process_inner_partition(outer_map, + inner_map, + outer_has_default, + inner_has_default, + inner_bi->null_index, + outer_bi->default_index, + jointype, + outer_bi->strategy, + next_index, + default_index, + &merged_index)) + return false; + } + *null_index = merged_index; } else { /* Both the relations have NULL partitions, try merging them. */ - *null_index = map_and_merge_partitions(outer_maps, - inner_maps, - outer_ni, - inner_ni, + *null_index = map_and_merge_partitions(outer_map, + inner_map, + outer_bi->null_index, + inner_bi->null_index, next_index); - if (*null_index < 0) - merged = false; + if (*null_index == -1) + return false; } - return merged; + return true; } diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out index b5b5c8a260..89f0c6a9ee 100644 --- a/src/test/regress/expected/partition_join.out +++ b/src/test/regress/expected/partition_join.out @@ -4769,3 +4769,182 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = Filter: (b = 0) (23 rows) +DROP TABLE plt1; +DROP TABLE plt2; +CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c); +CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0002'); +CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0003', '0004'); +INSERT INTO plt1 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (0, 2, 3, 4); +ANALYZE plt1; +CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c); +CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0001', '0002'); +CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0003', '0004'); +INSERT INTO plt2 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (1, 2, 3, 4); +ANALYZE plt2; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON (t1.c = t2.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND COALESCE(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a; + QUERY PLAN +----------------------------------------------------------------------------------------------- + Sort + Sort Key: t1.c, t1.a, t2.a + -> Append + -> Hash Full Join + Hash Cond: (t1.c = t2.c) + Filter: (((COALESCE(t1.a, 0) % 5) <> 3) AND ((COALESCE(t1.a, 0) % 5) <> 4)) + -> Seq Scan on plt1_p1 t1 + -> Hash + -> Seq Scan on plt2_p1 t2 + -> Hash Full Join + Hash Cond: (t1_1.c = t2_1.c) + Filter: (((COALESCE(t1_1.a, 0) % 5) <> 3) AND ((COALESCE(t1_1.a, 0) % 5) <> 4)) + -> Seq Scan on plt1_p2 t1_1 + -> Hash + -> Seq Scan on plt2_p2 t2_1 +(15 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON (t1.c = t2.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND COALESCE(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a; + a | c | a | c +----+------+----+------ + 0 | 0000 | | + 5 | 0000 | | + 10 | 0000 | | + 15 | 0000 | | + 20 | 0000 | | + 2 | 0002 | 2 | 0002 + 2 | 0002 | 7 | 0002 + 2 | 0002 | 12 | 0002 + 2 | 0002 | 17 | 0002 + 2 | 0002 | 22 | 0002 + 7 | 0002 | 2 | 0002 + 7 | 0002 | 7 | 0002 + 7 | 0002 | 12 | 0002 + 7 | 0002 | 17 | 0002 + 7 | 0002 | 22 | 0002 + 12 | 0002 | 2 | 0002 + 12 | 0002 | 7 | 0002 + 12 | 0002 | 12 | 0002 + 12 | 0002 | 17 | 0002 + 12 | 0002 | 22 | 0002 + 17 | 0002 | 2 | 0002 + 17 | 0002 | 7 | 0002 + 17 | 0002 | 12 | 0002 + 17 | 0002 | 17 | 0002 + 17 | 0002 | 22 | 0002 + 22 | 0002 | 2 | 0002 + 22 | 0002 | 7 | 0002 + 22 | 0002 | 12 | 0002 + 22 | 0002 | 17 | 0002 + 22 | 0002 | 22 | 0002 + | | 1 | 0001 + | | 6 | 0001 + | | 11 | 0001 + | | 16 | 0001 + | | 21 | 0001 +(35 rows) + +DROP TABLE plt1; +DROP TABLE plt2; +CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c); +CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0001', '0002'); +CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0003', '0004'); +INSERT INTO plt1 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i; +ANALYZE plt1; +CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c); +CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002'); +CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0003', '0004'); +INSERT INTO plt2 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (2, 3, 4); +ANALYZE plt2; +CREATE TABLE plt3 (a int, b int, c text) PARTITION BY LIST(c); +CREATE TABLE plt3_p1 PARTITION OF plt3 FOR VALUES IN ('0001'); +CREATE TABLE plt3_p2 PARTITION OF plt3 FOR VALUES IN ('0003', '0004'); +INSERT INTO plt3 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (1, 3, 4); +ANALYZE plt3; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON (t1.c = t2.c)) FULL JOIN plt3 t3 ON (t1.c = t3.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND coalesce(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a, t3.a; + QUERY PLAN +----------------------------------------------------------------------------------------------- + Sort + Sort Key: t1.c, t1.a, t2.a, t3.a + -> Append + -> Hash Full Join + Hash Cond: (t1.c = t3.c) + Filter: (((COALESCE(t1.a, 0) % 5) <> 3) AND ((COALESCE(t1.a, 0) % 5) <> 4)) + -> Hash Left Join + Hash Cond: (t1.c = t2.c) + -> Seq Scan on plt1_p1 t1 + -> Hash + -> Seq Scan on plt2_p1 t2 + -> Hash + -> Seq Scan on plt3_p1 t3 + -> Hash Full Join + Hash Cond: (t1_1.c = t3_1.c) + Filter: (((COALESCE(t1_1.a, 0) % 5) <> 3) AND ((COALESCE(t1_1.a, 0) % 5) <> 4)) + -> Hash Left Join + Hash Cond: (t1_1.c = t2_1.c) + -> Seq Scan on plt1_p2 t1_1 + -> Hash + -> Seq Scan on plt2_p2 t2_1 + -> Hash + -> Seq Scan on plt3_p2 t3_1 +(23 rows) + +SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON (t1.c = t2.c)) FULL JOIN plt3 t3 ON (t1.c = t3.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND coalesce(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a, t3.a; + a | c | a | c | a | c +----+------+----+------+----+------ + 0 | 0000 | | | | + 5 | 0000 | | | | + 10 | 0000 | | | | + 15 | 0000 | | | | + 20 | 0000 | | | | + 1 | 0001 | | | 1 | 0001 + 1 | 0001 | | | 6 | 0001 + 1 | 0001 | | | 11 | 0001 + 1 | 0001 | | | 16 | 0001 + 1 | 0001 | | | 21 | 0001 + 6 | 0001 | | | 1 | 0001 + 6 | 0001 | | | 6 | 0001 + 6 | 0001 | | | 11 | 0001 + 6 | 0001 | | | 16 | 0001 + 6 | 0001 | | | 21 | 0001 + 11 | 0001 | | | 1 | 0001 + 11 | 0001 | | | 6 | 0001 + 11 | 0001 | | | 11 | 0001 + 11 | 0001 | | | 16 | 0001 + 11 | 0001 | | | 21 | 0001 + 16 | 0001 | | | 1 | 0001 + 16 | 0001 | | | 6 | 0001 + 16 | 0001 | | | 11 | 0001 + 16 | 0001 | | | 16 | 0001 + 16 | 0001 | | | 21 | 0001 + 21 | 0001 | | | 1 | 0001 + 21 | 0001 | | | 6 | 0001 + 21 | 0001 | | | 11 | 0001 + 21 | 0001 | | | 16 | 0001 + 21 | 0001 | | | 21 | 0001 + 2 | 0002 | 2 | 0002 | | + 2 | 0002 | 7 | 0002 | | + 2 | 0002 | 12 | 0002 | | + 2 | 0002 | 17 | 0002 | | + 2 | 0002 | 22 | 0002 | | + 7 | 0002 | 2 | 0002 | | + 7 | 0002 | 7 | 0002 | | + 7 | 0002 | 12 | 0002 | | + 7 | 0002 | 17 | 0002 | | + 7 | 0002 | 22 | 0002 | | + 12 | 0002 | 2 | 0002 | | + 12 | 0002 | 7 | 0002 | | + 12 | 0002 | 12 | 0002 | | + 12 | 0002 | 17 | 0002 | | + 12 | 0002 | 22 | 0002 | | + 17 | 0002 | 2 | 0002 | | + 17 | 0002 | 7 | 0002 | | + 17 | 0002 | 12 | 0002 | | + 17 | 0002 | 17 | 0002 | | + 17 | 0002 | 22 | 0002 | | + 22 | 0002 | 2 | 0002 | | + 22 | 0002 | 7 | 0002 | | + 22 | 0002 | 12 | 0002 | | + 22 | 0002 | 17 | 0002 | | + 22 | 0002 | 22 | 0002 | | +(55 rows) + diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql index 34ae92135f..b0ab002eee 100644 --- a/src/test/regress/sql/partition_join.sql +++ b/src/test/regress/sql/partition_join.sql @@ -800,3 +800,47 @@ ANALYZE prt2; EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; + +DROP TABLE plt1; +DROP TABLE plt2; + +CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c); +CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0002'); +CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0003', '0004'); +INSERT INTO plt1 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (0, 2, 3, 4); +ANALYZE plt1; + +CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c); +CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0001', '0002'); +CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0003', '0004'); +INSERT INTO plt2 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (1, 2, 3, 4); +ANALYZE plt2; + +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON (t1.c = t2.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND COALESCE(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON (t1.c = t2.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND COALESCE(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a; + +DROP TABLE plt1; +DROP TABLE plt2; + +CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c); +CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0001', '0002'); +CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0003', '0004'); +INSERT INTO plt1 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i; +ANALYZE plt1; + +CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c); +CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002'); +CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0003', '0004'); +INSERT INTO plt2 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (2, 3, 4); +ANALYZE plt2; + +CREATE TABLE plt3 (a int, b int, c text) PARTITION BY LIST(c); +CREATE TABLE plt3_p1 PARTITION OF plt3 FOR VALUES IN ('0001'); +CREATE TABLE plt3_p2 PARTITION OF plt3 FOR VALUES IN ('0003', '0004'); +INSERT INTO plt3 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (1, 3, 4); +ANALYZE plt3; + +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON (t1.c = t2.c)) FULL JOIN plt3 t3 ON (t1.c = t3.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND coalesce(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a, t3.a; +SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON (t1.c = t2.c)) FULL JOIN plt3 t3 ON (t1.c = t3.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND coalesce(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a, t3.a;