/* One bound of a hash partition */ typedefstruct PartitionHashBound
{ int modulus; int remainder; int index;
} PartitionHashBound;
/* One value coming from some (index'th) list partition */ typedefstruct PartitionListValue
{ int index;
Datum value;
} PartitionListValue;
/* One bound of a range partition */ typedefstruct PartitionRangeBound
{ int index;
Datum *datums; /* range bound datums */
PartitionRangeDatumKind *kind; /* the kind of each datum */ bool lower; /* this is the lower (vs upper) bound */
} PartitionRangeBound;
/* *Mappingfrompartitionsofajoiningrelationtopartitionsofajoin *relationbeingcomputed(a.k.amergedpartitions)
*/ typedefstruct PartitionMap
{ 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 re-map partitions? */ int *old_indexes; /* old indexes of merged partitions if
* did_remapping */
} PartitionMap;
/* Macro for comparing two range bounds */ #define compare_range_bounds(partnatts, partsupfunc, partcollations, \
bound1, bound2) \
(partition_rbound_cmp(partnatts, partsupfunc, partcollations, \
(bound1)->datums, (bound1)->kind, (bound1)->lower, \
bound2))
static int32 qsort_partition_hbound_cmp(constvoid *a, constvoid *b); static int32 qsort_partition_list_value_cmp(constvoid *a, constvoid *b, void *arg); static int32 qsort_partition_rbound_cmp(constvoid *a, constvoid *b, void *arg); static PartitionBoundInfo create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping); 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,
Oid *partcollation,
RelOptInfo *outer_rel,
RelOptInfo *inner_rel,
JoinType jointype,
List **outer_parts,
List **inner_parts); static PartitionBoundInfo merge_range_bounds(int partnatts,
FmgrInfo *partsupfuncs,
Oid *partcollations,
RelOptInfo *outer_rel,
RelOptInfo *inner_rel,
JoinType jointype,
List **outer_parts,
List **inner_parts); staticvoid init_partition_map(RelOptInfo *rel, PartitionMap *map); staticvoid free_partition_map(PartitionMap *map); staticbool is_dummy_partition(RelOptInfo *rel, int part_index); staticint merge_matching_partitions(PartitionMap *outer_map,
PartitionMap *inner_map, int outer_index, int inner_index, int *next_index); staticint 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, int *next_index, int *default_index); staticint 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, int *next_index, int *default_index); staticvoid merge_null_partitions(PartitionMap *outer_map,
PartitionMap *inner_map, bool outer_has_null, bool inner_has_null, int outer_null, int inner_null,
JoinType jointype, int *next_index, int *null_index); staticvoid merge_default_partitions(PartitionMap *outer_map,
PartitionMap *inner_map, bool outer_has_default, bool inner_has_default, int outer_default, int inner_default,
JoinType jointype, int *next_index, int *default_index); staticint merge_partition_with_dummy(PartitionMap *map, int index, int *next_index); staticvoid fix_merged_indexes(PartitionMap *outer_map,
PartitionMap *inner_map, int nmerged, List *merged_indexes); staticvoid generate_matching_part_pairs(RelOptInfo *outer_rel,
RelOptInfo *inner_rel,
PartitionMap *outer_map,
PartitionMap *inner_map, int nmerged,
List **outer_parts,
List **inner_parts); static PartitionBoundInfo build_merged_partition_bounds(char strategy,
List *merged_datums,
List *merged_kinds,
List *merged_indexes, int null_index, int default_index); staticint get_range_partition(RelOptInfo *rel,
PartitionBoundInfo bi, int *lb_pos,
PartitionRangeBound *lb,
PartitionRangeBound *ub); staticint get_range_partition_internal(PartitionBoundInfo bi, int *lb_pos,
PartitionRangeBound *lb,
PartitionRangeBound *ub); staticbool compare_range_partitions(int partnatts, FmgrInfo *partsupfuncs,
Oid *partcollations,
PartitionRangeBound *outer_lb,
PartitionRangeBound *outer_ub,
PartitionRangeBound *inner_lb,
PartitionRangeBound *inner_ub, int *lb_cmpval, int *ub_cmpval); staticvoid get_merged_range_bounds(int partnatts, FmgrInfo *partsupfuncs,
Oid *partcollations, JoinType jointype,
PartitionRangeBound *outer_lb,
PartitionRangeBound *outer_ub,
PartitionRangeBound *inner_lb,
PartitionRangeBound *inner_ub, int lb_cmpval, int ub_cmpval,
PartitionRangeBound *merged_lb,
PartitionRangeBound *merged_ub); staticvoid add_merged_range_bounds(int partnatts, FmgrInfo *partsupfuncs,
Oid *partcollations,
PartitionRangeBound *merged_lb,
PartitionRangeBound *merged_ub, int merged_index,
List **merged_datums,
List **merged_kinds,
List **merged_indexes); static PartitionRangeBound *make_one_partition_rbound(PartitionKey key, int index,
List *datums, bool lower); static int32 partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2); static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
Oid *partcollation, Datum *datums1,
PartitionRangeDatumKind *kind1, bool lower1,
PartitionRangeBound *b2); staticint partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
Oid *partcollation,
PartitionBoundInfo boundinfo,
PartitionRangeBound *probe, int32 *cmpval); static Expr *make_partition_op_expr(PartitionKey key, int keynum,
uint16 strategy, Expr *arg1, Expr *arg2); static Oid get_partition_operator(PartitionKey key, int col,
StrategyNumber strategy, bool *need_relabel); static List *get_qual_for_hash(Relation parent, PartitionBoundSpec *spec); static List *get_qual_for_list(Relation parent, PartitionBoundSpec *spec); static List *get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default); staticvoid get_range_key_properties(PartitionKey key, int keynum,
PartitionRangeDatum *ldatum,
PartitionRangeDatum *udatum,
ListCell **partexprs_item,
Expr **keyCol, Const **lower_val, Const **upper_val); static List *get_range_nulltest(PartitionKey key);
/* *get_qual_from_partbound *Givenaparsernodeforpartitionbound,returnthelistofexecutable *expressionsaspartitionconstraint
*/
List *
get_qual_from_partbound(Relation parent, PartitionBoundSpec *spec)
{
PartitionKey key = RelationGetPartitionKey(parent);
List *my_qual = NIL;
/* *create_hash_bounds *CreateaPartitionBoundInfoforahashpartitionedtable
*/ static PartitionBoundInfo
create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts,
PartitionKey key, int **mapping)
{
PartitionBoundInfo boundinfo;
PartitionHashBound *hbounds; int i; int greatest_modulus;
Datum *boundDatums;
boundinfo = (PartitionBoundInfoData *)
palloc0(sizeof(PartitionBoundInfoData));
boundinfo->strategy = key->strategy; /* No special hash partitions. */
boundinfo->null_index = -1;
boundinfo->default_index = -1;
/* *Forhashpartitioning,thereareasmanydatums(modulusandremainder *pairs)astherearepartitions.Indexesaresimplyvaluesrangingfrom *0to(nparts-1).
*/ for (i = 0; i < nparts; i++)
{ int modulus = hbounds[i].modulus; int remainder = hbounds[i].remainder;
/* *create_list_bounds *CreateaPartitionBoundInfoforalistpartitionedtable
*/ static PartitionBoundInfo
create_list_bounds(PartitionBoundSpec **boundspecs, int nparts,
PartitionKey key, int **mapping)
{
PartitionBoundInfo boundinfo;
PartitionListValue *all_values; int i; int j; int ndatums; int next_index = 0; int default_index = -1; int null_index = -1;
Datum *boundDatums;
boundinfo = (PartitionBoundInfoData *)
palloc0(sizeof(PartitionBoundInfoData));
boundinfo->strategy = key->strategy; /* Will be set correctly below. */
boundinfo->null_index = -1;
boundinfo->default_index = -1;
/* Create a unified list of non-null values across all partitions. */ for (j = 0, i = 0; i < nparts; i++)
{
PartitionBoundSpec *spec = boundspecs[i];
ListCell *c;
if (spec->strategy != PARTITION_STRATEGY_LIST)
elog(ERROR, "invalid strategy in partition bound spec");
/* *Copyvalues.Canonicalindexesarevaluesrangingfrom0to(nparts- *1)assignedtoeachpartitionsuchthatalldatumsofagivenpartition *receivethesamevalue.Thevalueforagivenpartitionistheindexof *thatpartition'ssmallestdatumintheall_values[]array.
*/ for (i = 0; i < ndatums; i++)
{ int orig_index = all_values[i].index;
/* *Theremustbemultiplepartitionstohaveanyinterleavedpartitions, *otherwisethere'snothingtointerleavewith.
*/ if (nparts > 1)
{ /* *Short-circuitchecktoseeifonly1Datumisallowedper *partition.Whenthisistruethere'snoneedtodothemore *expensivecheckstolookforinterleavedvalues.
*/ if (boundinfo->ndatums +
partition_bound_accepts_nulls(boundinfo) +
partition_bound_has_default(boundinfo) != nparts)
{ int last_index = -1;
/* *SincetheindexesarrayissortedinDatumorder,ifany *partitionsareinterleavedthenitwillshowupbythe *partitionindexesnotbeinginascendingorder.Herewecheck *forthatandrecordallpartitionsthatareoutoforder.
*/ for (i = 0; i < boundinfo->nindexes; i++)
{ int index = boundinfo->indexes[i];
if (index < last_index)
boundinfo->interleaved_parts = bms_add_member(boundinfo->interleaved_parts,
index);
/* All partitions must now have been assigned canonical indexes. */
Assert(next_index == nparts); return boundinfo;
}
/* *create_range_bounds *CreateaPartitionBoundInfoforarangepartitionedtable
*/ static PartitionBoundInfo
create_range_bounds(PartitionBoundSpec **boundspecs, int nparts,
PartitionKey key, int **mapping)
{
PartitionBoundInfo boundinfo;
PartitionRangeBound **rbounds = NULL;
PartitionRangeBound **all_bounds,
*prev; int i,
k,
partnatts; int ndatums = 0; int default_index = -1; int next_index = 0;
Datum *boundDatums;
PartitionRangeDatumKind *boundKinds;
boundinfo = (PartitionBoundInfoData *)
palloc0(sizeof(PartitionBoundInfoData));
boundinfo->strategy = key->strategy; /* There is no special null-accepting range partition. */
boundinfo->null_index = -1; /* Will be set correctly below. */
boundinfo->default_index = -1;
/* Create a unified list of range bounds across all the partitions. */
ndatums = 0; for (i = 0; i < nparts; i++)
{
PartitionBoundSpec *spec = boundspecs[i];
PartitionRangeBound *lower,
*upper;
if (spec->strategy != PARTITION_STRATEGY_RANGE)
elog(ERROR, "invalid strategy in partition bound spec");
/* Set the canonical value for default_index, if any. */ if (default_index != -1)
{
Assert(default_index >= 0 && (*mapping)[default_index] == -1);
(*mapping)[default_index] = next_index++;
boundinfo->default_index = (*mapping)[default_index];
}
/* The extra -1 element. */
Assert(i == ndatums);
boundinfo->indexes[i] = -1;
/* All partitions must now have been assigned canonical indexes. */
Assert(next_index == nparts); return boundinfo;
}
if (b1->null_index != b2->null_index) returnfalse;
if (b1->default_index != b2->default_index) returnfalse;
/* For all partition strategies, the indexes[] arrays have to match */ for (i = 0; i < b1->nindexes; i++)
{ if (b1->indexes[i] != b2->indexes[i]) returnfalse;
}
/* Finally, compare the datums[] arrays */ if (b1->strategy == PARTITION_STRATEGY_HASH)
{ /* *Wearrangethepartitionsintheascendingorderoftheirmoduli *andremainders.Alsoeverymodulusisfactorofnextlarger *modulus.Thereforewecansafelystoreindexofagivenpartition *inindexesarrayatremainderofthatpartition.Alsoentriesat *(remainder+N*modulus)positionsinindexesarrayareallsame *for(modulus,remainder)specificationforanypartition.Thusthe *datumsarraysfromthegivenboundsarethesame,ifandonlyif *theirindexesarraysarethesame.So,itsufficestocomparethe *indexesarrays. * *Nonethelessmakesurethattheboundsareindeedthesamewhenthe *indexesmatch.Hashpartitionboundstoresmodulusandremainder *atb1->datums[i][0]andb1->datums[i][1]positionrespectively.
*/ #ifdef USE_ASSERT_CHECKING for (i = 0; i < b1->ndatums; i++)
Assert((b1->datums[i][0] == b2->datums[i][0] &&
b1->datums[i][1] == b2->datums[i][1])); #endif
} else
{ for (i = 0; i < b1->ndatums; i++)
{ int j;
for (j = 0; j < partnatts; j++)
{ /* For range partitions, the bounds might not be finite. */ if (b1->kind != NULL)
{ /* The different kinds of bound all differ from each other */ if (b1->kind[i][j] != b2->kind[i][j]) returnfalse;
/* *Non-finiteboundsareequalwithoutfurther *examination.
*/ if (b1->kind[i][j] != PARTITION_RANGE_DATUM_VALUE) continue;
}
/* Move to the next pair of list values. */
outer_pos++;
inner_pos++;
} elseif (cmpval < 0)
{ /* A list value missing from the inner side. */
Assert(outer_pos < outer_bi->ndatums);
/* Move to the next list value on the outer side. */
outer_pos++;
} else
{ /* A list value missing from the outer side. */
Assert(cmpval > 0);
Assert(inner_pos < inner_bi->ndatums);
/* *IftheNULLpartitions(ifany)havebeenprovenempty,deemthem *non-existent.
*/ if (outer_has_null &&
is_dummy_partition(outer_rel, outer_bi->null_index))
outer_has_null = false; if (inner_has_null &&
is_dummy_partition(inner_rel, inner_bi->null_index))
inner_has_null = false;
/* Merge the NULL partitions if any. */ if (outer_has_null || inner_has_null)
merge_null_partitions(&outer_map, &inner_map,
outer_has_null, inner_has_null,
outer_bi->null_index, inner_bi->null_index,
jointype, &next_index, &null_index); else
Assert(null_index == -1);
/* Merge the default partitions if any. */ if (outer_has_default || inner_has_default)
merge_default_partitions(&outer_map, &inner_map,
outer_has_default, inner_has_default,
outer_default, inner_default,
jointype, &next_index, &default_index); else
Assert(default_index == -1);
/* If we have merged partitions, create the partition bounds. */ if (next_index > 0)
{ /* 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 partitions from inputs. */
generate_matching_part_pairs(outer_rel, inner_rel,
&outer_map, &inner_map,
next_index,
outer_parts, inner_parts);
Assert(*outer_parts != NIL);
Assert(*inner_parts != NIL);
Assert(list_length(*outer_parts) == list_length(*inner_parts));
Assert(list_length(*outer_parts) <= next_index);
/* Make a PartitionBoundInfo struct to return. */
merged_bounds = build_merged_partition_bounds(outer_bi->strategy,
merged_datums,
NIL,
merged_indexes,
null_index,
default_index);
Assert(merged_bounds);
}
cleanup: /* Free local memory before returning. */
list_free(merged_datums);
list_free(merged_indexes);
free_partition_map(&outer_map);
free_partition_map(&inner_map);
return merged_bounds;
}
/* *merge_range_bounds *Createthepartitionboundsforajoinrelationbetweenrange *partitionedtables,ifpossible * *Inthisfunctionwetrytofindsetsofoverlappingpartitionsfromboth *sidesbycomparingrangesstoredintheirpartitionbounds.Sincethe *rangesappearintheascendingorder,analgorithmsimilartomergejoinis *usedforthat.Ifapartitionononesidedoesn'thaveanoverlapping *partitionontheotherside,thealgorithmtriestomatchitwiththe *defaultpartitionontheothersideifany;ifnot,thealgorithmtriesto *matchitwithadummypartitionontheothersideifit'sonthe *non-nullablesideofanouterjoin.Also,ifbothsideshavethedefault *partitions,thealgorithmtriestomatchthemwitheachother.Wegiveup *ifthealgorithmfindsapartitionoverlappingmultiplepartitionsonthe *otherside,whichisthescenariothecurrentimplementationofpartitioned *joincan'thandle.
*/ static PartitionBoundInfo
merge_range_bounds(int partnatts, FmgrInfo *partsupfuncs,
Oid *partcollations,
RelOptInfo *outer_rel, RelOptInfo *inner_rel,
JoinType jointype,
List **outer_parts, List **inner_parts)
{
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); bool inner_has_default = partition_bound_has_default(inner_bi); int outer_default = outer_bi->default_index; int inner_default = inner_bi->default_index;
PartitionMap outer_map;
PartitionMap inner_map; int outer_index; int inner_index; int outer_lb_pos; int inner_lb_pos;
PartitionRangeBound outer_lb;
PartitionRangeBound outer_ub;
PartitionRangeBound inner_lb;
PartitionRangeBound inner_ub; int next_index = 0; int default_index = -1;
List *merged_datums = NIL;
List *merged_kinds = NIL;
List *merged_indexes = NIL;
/* Get the range bounds of the merged partition. */
get_merged_range_bounds(partnatts, partsupfuncs,
partcollations, jointype,
&outer_lb, &outer_ub,
&inner_lb, &inner_ub,
lb_cmpval, ub_cmpval,
&merged_lb, &merged_ub);
/* Save the upper bounds of both partitions for use below. */
save_outer_ub = outer_ub;
save_inner_ub = inner_ub;
/* Move to the next pair of ranges. */
outer_index = get_range_partition(outer_rel, outer_bi, &outer_lb_pos,
&outer_lb, &outer_ub);
inner_index = get_range_partition(inner_rel, inner_bi, &inner_lb_pos,
&inner_lb, &inner_ub);
/* The outer partition should not have been merged yet. */
Assert(outer_index >= 0);
Assert(outer_map.merged_indexes[outer_index] == -1 &&
outer_map.merged[outer_index] == false);
/*
* If the inner side has the default partition, or this is an
* outer join, try to assign a merged partition to the outer
* partition (see process_outer_partition()). Otherwise, the
* outer partition will not contribute to the result.
*/
if (inner_has_default || IS_OUTER_JOIN(jointype))
{
merged_index = process_outer_partition(&outer_map,
&inner_map,
outer_has_default,
inner_has_default,
outer_index,
inner_default,
jointype,
&next_index,
&default_index);
if (merged_index == -1)
goto cleanup;
merged_lb = outer_lb;
merged_ub = outer_ub;
}
/* Move to the next range on the outer side. */
outer_index = get_range_partition(outer_rel, outer_bi, &outer_lb_pos,
&outer_lb, &outer_ub);
}
else
{
/* A non-overlapping inner range. */
Assert(ub_cmpval > 0);
/* The inner partition should not have been merged yet. */
Assert(inner_index >= 0);
Assert(inner_map.merged_indexes[inner_index] == -1 &&
inner_map.merged[inner_index] == false);
/*
* If the outer side has the default partition, or this is a FULL
* join, try to assign a merged partition to the inner partition
* (see process_inner_partition()). Otherwise, the inner
* partition will not contribute to the result.
*/
if (outer_has_default || jointype == JOIN_FULL)
{
merged_index = process_inner_partition(&outer_map,
&inner_map,
outer_has_default,
inner_has_default,
inner_index,
outer_default,
jointype,
&next_index,
&default_index);
if (merged_index == -1)
goto cleanup;
merged_lb = inner_lb;
merged_ub = inner_ub;
}
/* Move to the next range on the inner side. */
inner_index = get_range_partition(inner_rel, inner_bi, &inner_lb_pos,
&inner_lb, &inner_ub);
}
/*
* If we assigned a merged partition, add the range bounds and index
* of the merged partition if appropriate.
*/
if (merged_index >= 0 && merged_index != default_index)
add_merged_range_bounds(partnatts, partsupfuncs, partcollations,
&merged_lb, &merged_ub, merged_index,
&merged_datums, &merged_kinds,
&merged_indexes);
}
/* Merge the default partitions if any. */
if (outer_has_default || inner_has_default)
merge_default_partitions(&outer_map, &inner_map,
outer_has_default, inner_has_default,
outer_default, inner_default,
jointype, &next_index, &default_index);
else
Assert(default_index == -1);
/* If we have merged partitions, create the partition bounds. */
if (next_index > 0)
{
/*
* Unlike the case of list partitioning, we wouldn't have re-merged
* partitions, so did_remapping should be left alone.
*/
Assert(!outer_map.did_remapping);
Assert(!inner_map.did_remapping);
/* Use maps to match partitions from inputs. */
generate_matching_part_pairs(outer_rel, inner_rel,
&outer_map, &inner_map,
next_index,
outer_parts, inner_parts);
Assert(*outer_parts != NIL);
Assert(*inner_parts != NIL);
Assert(list_length(*outer_parts) == list_length(*inner_parts));
Assert(list_length(*outer_parts) == next_index);
/* Make a PartitionBoundInfo struct to return. */
merged_bounds = build_merged_partition_bounds(outer_bi->strategy,
merged_datums,
merged_kinds,
merged_indexes,
-1,
default_index);
Assert(merged_bounds);
}
cleanup:
/* Free local memory before returning. */
list_free(merged_datums);
list_free(merged_kinds);
list_free(merged_indexes);
free_partition_map(&outer_map);
free_partition_map(&inner_map);
return merged_bounds;
}
/*
* init_partition_map
* Initialize a PartitionMap struct for given relation
*/
static void
init_partition_map(RelOptInfo *rel, PartitionMap *map)
{
int nparts = rel->nparts;
int i;
/*
* merge_matching_partitions
* Try to merge given outer/inner partitions, and return the index of a
* merged partition produced from them if successful, -1 otherwise
*
* If the merged partition is newly created, *next_index is incremented.
*/
static int
merge_matching_partitions(PartitionMap *outer_map, PartitionMap *inner_map,
int outer_index, int inner_index, int *next_index)
{
int outer_merged_index;
int inner_merged_index;
bool outer_merged;
bool inner_merged;
/*
* Handle cases where we have already assigned a merged partition to each
* of the given partitions.
*/
if (outer_merged_index >= 0 && inner_merged_index >= 0)
{
/*
* If the merged partitions are the same, no need to do anything;
* return the index of the merged partitions. Otherwise, if each of
* the given partitions has been merged with a dummy partition on the
* other side, re-map them to either of the two merged partitions.
* Otherwise, they can't be merged, so return -1.
*/
if (outer_merged_index == inner_merged_index)
{
Assert(outer_merged);
Assert(inner_merged);
return outer_merged_index;
}
if (!outer_merged && !inner_merged)
{
/*
* This can only happen for a list-partitioning case. We re-map
* them to the merged partition with the smaller of the two merged
* indexes to preserve the property that the canonical order of
* list partitions is determined by the indexes assigned to the
* smallest list value of each partition.
*/
if (outer_merged_index < inner_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 of the given partitions should not have yet been merged. */
Assert(outer_merged_index == -1 || inner_merged_index == -1);
/*
* If neither of them has been merged, merge them. Otherwise, if one has
* been merged with a dummy partition on the other side (and the other
* hasn't yet been merged with anything), re-merge them. Otherwise, they
* can't be merged, so return -1.
*/
if (outer_merged_index == -1 && inner_merged_index == -1)
{
int merged_index = *next_index;
/*
* process_outer_partition
* Try to assign given outer partition a merged partition, and return the
* index of the merged partition if successful, -1 otherwise
*
* If the partition is newly created, *next_index is incremented. Also, if it
* is the default partition of the join relation, *default_index is set to the
* index if not already done.
*/
static int
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,
int *next_index,
int *default_index)
{
int merged_index = -1;
Assert(outer_index >= 0);
/*
* If the inner side has the default partition, a row from the outer
* partition might find its join partner in the default partition; try
* merging the outer partition with the default partition. Otherwise,
* this should be an outer join, in which case the outer partition has to
* be scanned all the way anyway; merge the outer partition with a dummy
* partition on the other side.
*/
if (inner_has_default)
{
Assert(inner_default >= 0);
/*
* If the outer side has the default partition as well, the default
* partition on the inner side will have two matching partitions on
* the other side: the outer partition and the default partition on
* the outer side. Partitionwise join doesn't handle this scenario
* yet.
*/
if (outer_has_default)
return -1;
/*
* If this is a FULL join, the default partition on the inner side has
* to be scanned all the way anyway, so the resulting partition will
* contain all key values from the default partition, which any other
* partition of the join relation will not contain. Thus the
* resulting partition will act as the default partition of the join
* relation; record the index in *default_index if not already done.
*/
if (jointype == JOIN_FULL)
{
if (*default_index == -1)
*default_index = merged_index;
else
Assert(*default_index == merged_index);
}
}
else
{
Assert(IS_OUTER_JOIN(jointype));
Assert(jointype != JOIN_RIGHT);
/* If we have already assigned a partition, no need to do anything. */
merged_index = outer_map->merged_indexes[outer_index];
if (merged_index == -1)
merged_index = merge_partition_with_dummy(outer_map, outer_index,
next_index);
}
return merged_index;
}
/*
* process_inner_partition
* Try to assign given inner partition a merged partition, and return the
* index of the merged partition if successful, -1 otherwise
*
* If the partition is newly created, *next_index is incremented. Also, if it
* is the default partition of the join relation, *default_index is set to the
* index if not already done.
*/
static int
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,
int *next_index,
int *default_index)
{
int merged_index = -1;
Assert(inner_index >= 0);
/*
* If the outer side has the default partition, a row from the inner
* partition might find its join partner in the default partition; try
* merging the inner partition with the default partition. Otherwise,
* this should be a FULL join, in which case the inner partition has to be
* scanned all the way anyway; merge the inner partition with a dummy
* partition on the other side.
*/
if (outer_has_default)
{
Assert(outer_default >= 0);
/*
* If the inner side has the default partition as well, the default
* partition on the outer side will have two matching partitions on
* the other side: the inner partition and the default partition on
* the inner side. Partitionwise join doesn't handle this scenario
* yet.
*/
if (inner_has_default)
return -1;
/*
* If this is an outer join, the default partition on the outer side
* has to be scanned all the way anyway, so the resulting partition
* will contain all key values from the default partition, which any
* other partition of the join relation will not contain. Thus the
* resulting partition will act as the default partition of the join
* relation; record the index in *default_index if not already done.
*/
if (IS_OUTER_JOIN(jointype))
{
Assert(jointype != JOIN_RIGHT);
if (*default_index == -1)
*default_index = merged_index;
else
Assert(*default_index == merged_index);
}
}
else
{
Assert(jointype == JOIN_FULL);
/* If we have already assigned a partition, no need to do anything. */
merged_index = inner_map->merged_indexes[inner_index];
if (merged_index == -1)
merged_index = merge_partition_with_dummy(inner_map, inner_index,
next_index);
}
return merged_index;
}
/*
* merge_null_partitions
* Merge the NULL partitions from a join's outer and inner sides.
*
* If the merged partition produced from them is the NULL partition of the join
* relation, *null_index is set to the index of the merged partition.
*
* Note: We assume here that the join clause for a partitioned join is strict
* because have_partkey_equi_join() requires that the corresponding operator
* be mergejoinable, and we currently assume that mergejoinable operators are
* strict (see MJEvalOuterValues()/MJEvalInnerValues()).
*/
static void
merge_null_partitions(PartitionMap *outer_map,
PartitionMap *inner_map,
bool outer_has_null,
bool inner_has_null,
int outer_null,
int inner_null,
JoinType jointype,
int *next_index,
int *null_index)
{
bool consider_outer_null = false;
bool consider_inner_null = false;
/*
* Check whether the NULL partitions have already been merged and if so,
* set the consider_outer_null/consider_inner_null flags.
*/
if (outer_has_null)
{
Assert(outer_null >= 0 && outer_null < outer_map->nparts);
if (outer_map->merged_indexes[outer_null] == -1)
consider_outer_null = true;
}
if (inner_has_null)
{
Assert(inner_null >= 0 && inner_null < inner_map->nparts);
if (inner_map->merged_indexes[inner_null] == -1)
consider_inner_null = true;
}
/* If both flags are set false, we don't need to do anything. */
if (!consider_outer_null && !consider_inner_null)
return;
if (consider_outer_null && !consider_inner_null)
{
Assert(outer_has_null);
/*
* If this is an outer join, the NULL partition on the outer side has
* to be scanned all the way anyway; merge the NULL partition with a
* dummy partition on the other side. In that case
* consider_outer_null means that the NULL partition only contains
* NULL values as the key values, so the merged partition will do so;
* treat it as the NULL partition of the join relation.
*/
if (IS_OUTER_JOIN(jointype))
{
Assert(jointype != JOIN_RIGHT);
*null_index = merge_partition_with_dummy(outer_map, outer_null,
next_index);
}
}
else if (!consider_outer_null && consider_inner_null)
{
Assert(inner_has_null);
/*
* If this is a FULL join, the NULL partition on the inner side has to
* be scanned all the way anyway; merge the NULL partition with a
* dummy partition on the other side. In that case
* consider_inner_null means that the NULL partition only contains
* NULL values as the key values, so the merged partition will do so;
* treat it as the NULL partition of the join relation.
*/
if (jointype == JOIN_FULL)
*null_index = merge_partition_with_dummy(inner_map, inner_null,
next_index);
}
else
{
Assert(consider_outer_null && consider_inner_null);
Assert(outer_has_null);
Assert(inner_has_null);
/*
* If this is an outer join, the NULL partition on the outer side (and
* that on the inner side if this is a FULL join) have to be scanned
* all the way anyway, so merge them. Note that each of the NULL
* partitions isn't merged yet, so they should be merged successfully.
* Like the above, each of the NULL partitions only contains NULL
* values as the key values, so the merged partition will do so; treat
* it as the NULL partition of the join relation.
*
* Note: if this an INNER/SEMI join, the join clause will never be
* satisfied by two NULL values (see comments above), so both the NULL
* partitions can be eliminated.
*/
if (IS_OUTER_JOIN(jointype))
{
Assert(jointype != JOIN_RIGHT);
*null_index = merge_matching_partitions(outer_map, inner_map,
outer_null, inner_null,
next_index);
Assert(*null_index >= 0);
}
}
}
/*
* merge_default_partitions
* Merge the default partitions from a join's outer and inner sides.
*
* If the merged partition produced from them is the default partition of the
* join relation, *default_index is set to the index of the merged partition.
*/
static void
merge_default_partitions(PartitionMap *outer_map,
PartitionMap *inner_map,
bool outer_has_default,
bool inner_has_default,
int outer_default,
int inner_default,
JoinType jointype,
int *next_index,
int *default_index)
{
int outer_merged_index = -1;
int inner_merged_index = -1;
Assert(outer_has_default || inner_has_default);
/* Get the merged partition indexes for the default partitions. */
if (outer_has_default)
{
Assert(outer_default >= 0 && outer_default < outer_map->nparts);
outer_merged_index = outer_map->merged_indexes[outer_default];
}
if (inner_has_default)
{
Assert(inner_default >= 0 && inner_default < inner_map->nparts);
inner_merged_index = inner_map->merged_indexes[inner_default];
}
if (outer_has_default && !inner_has_default)
{
/*
* If this is an outer join, the default partition on the outer side
* has to be scanned all the way anyway; if we have not yet assigned a
* partition, merge the default partition with a dummy partition on
* the other side. The merged partition will act as the default
* partition of the join relation (see comments in
* process_inner_partition()).
*/
if (IS_OUTER_JOIN(jointype))
{
Assert(jointype != JOIN_RIGHT);
if (outer_merged_index == -1)
{
Assert(*default_index == -1);
*default_index = merge_partition_with_dummy(outer_map,
outer_default,
next_index);
}
else
Assert(*default_index == outer_merged_index);
}
else
Assert(*default_index == -1);
}
else if (!outer_has_default && inner_has_default)
{
/*
* If this is a FULL join, the default partition on the inner side has
* to be scanned all the way anyway; if we have not yet assigned a
* partition, merge the default partition with a dummy partition on
* the other side. The merged partition will act as the default
* partition of the join relation (see comments in
* process_outer_partition()).
*/
if (jointype == JOIN_FULL)
{
if (inner_merged_index == -1)
{
Assert(*default_index == -1);
*default_index = merge_partition_with_dummy(inner_map,
inner_default,
next_index);
}
else
Assert(*default_index == inner_merged_index);
}
else
Assert(*default_index == -1);
}
else
{
Assert(outer_has_default && inner_has_default);
/*
* The default partitions have to be joined with each other, so merge
* them. Note that each of the default partitions isn't merged yet
* (see, process_outer_partition()/process_inner_partition()), so they
* should be merged successfully. The merged partition will act as
* the default partition of the join relation.
*/
Assert(outer_merged_index == -1);
Assert(inner_merged_index == -1);
Assert(*default_index == -1);
*default_index = merge_matching_partitions(outer_map,
inner_map,
outer_default,
inner_default,
next_index);
Assert(*default_index >= 0);
}
}
/*
* merge_partition_with_dummy
* Assign given partition a new partition of a join relation
*
* Note: The caller assumes that the given partition doesn't have a non-dummy
* matching partition on the other side, but if the given partition finds the
* matching partition later, we will adjust the assignment.
*/
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;
}
/*
* fix_merged_indexes
* Adjust merged indexes of re-merged partitions
*/
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;
Assert(nmerged > 0);
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
* Generate a pair of lists of partitions that produce merged partitions
*
* The lists of partitions are built in the order of merged partition indexes,
* and returned in *outer_parts and *inner_parts.
*/
static void
generate_matching_part_pairs(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
PartitionMap *outer_map, PartitionMap *inner_map,
int nmerged,
List **outer_parts, List **inner_parts)
{
int outer_nparts = outer_map->nparts;
int inner_nparts = inner_map->nparts;
int *outer_indexes;
int *inner_indexes;
int max_nparts;
int i;
outer_indexes = (int *) palloc(sizeof(int) * nmerged);
inner_indexes = (int *) palloc(sizeof(int) * nmerged);
for (i = 0; i < nmerged; i++)
outer_indexes[i] = inner_indexes[i] = -1;
/* Set pairs of matching partitions. */
Assert(outer_nparts == outer_rel->nparts);
Assert(inner_nparts == inner_rel->nparts);
max_nparts = Max(outer_nparts, inner_nparts);
for (i = 0; i < max_nparts; i++)
{
if (i < outer_nparts)
{
int merged_index = outer_map->merged_indexes[i];
if (merged_index >= 0)
{
Assert(merged_index < nmerged);
outer_indexes[merged_index] = i;
}
}
if (i < inner_nparts)
{
int merged_index = inner_map->merged_indexes[i];
/* Build the list pairs. */
for (i = 0; i < nmerged; i++)
{
int outer_index = outer_indexes[i];
int inner_index = inner_indexes[i];
/*
* If both partitions are dummy, it means the merged partition that
* had been assigned to the outer/inner partition was removed when
* re-merging the outer/inner partition in
* merge_matching_partitions(); ignore the merged partition.
*/
if (outer_index == -1 && inner_index == -1)
continue;
/*
* build_merged_partition_bounds
* Create a PartitionBoundInfo struct from merged partition bounds
*/
static PartitionBoundInfo
build_merged_partition_bounds(char strategy, List *merged_datums,
List *merged_kinds, List *merged_indexes,
int null_index, int default_index)
{
PartitionBoundInfo merged_bounds;
int ndatums = list_length(merged_datums);
int pos;
ListCell *lc;
/* There are ndatums+1 indexes in the case of range partitioning. */
merged_indexes = lappend_int(merged_indexes, -1);
ndatums++;
}
else
{
Assert(strategy == PARTITION_STRATEGY_LIST);
Assert(merged_kinds == NIL);
merged_bounds->kind = NULL;
}
/* interleaved_parts is always NULL for join relations. */
merged_bounds->interleaved_parts = NULL;
/*
* get_range_partition
* Get the next non-dummy partition of a range-partitioned relation,
* returning the index of that partition
*
* *lb and *ub are set to the lower and upper bounds of that partition
* respectively, and *lb_pos is advanced to the next lower bound, if any.
*/
static int
get_range_partition(RelOptInfo *rel,
PartitionBoundInfo bi,
int *lb_pos,
PartitionRangeBound *lb,
PartitionRangeBound *ub)
{
int part_index;
Assert(bi->strategy == PARTITION_STRATEGY_RANGE);
do
{
part_index = get_range_partition_internal(bi, lb_pos, lb, ub);
if (part_index == -1)
return -1;
} while (is_dummy_partition(rel, part_index));
return part_index;
}
static int
get_range_partition_internal(PartitionBoundInfo bi,
int *lb_pos,
PartitionRangeBound *lb,
PartitionRangeBound *ub)
{
/* Return the index as -1 if we've exhausted all lower bounds. */
if (*lb_pos >= bi->ndatums)
return -1;
/* A lower bound should have at least one more bound after it. */
Assert(*lb_pos + 1 < bi->ndatums);
/* The index assigned to an upper bound should be valid. */
Assert(ub->index >= 0);
/*
* Advance the position to the next lower bound. If there are no bounds
* left beyond the upper bound, we have reached the last lower bound.
*/
if (*lb_pos + 2 >= bi->ndatums)
*lb_pos = bi->ndatums;
else
{
/*
* If the index assigned to the bound next to the upper bound isn't
* valid, that is the next lower bound; else, the upper bound is also
* the lower bound of the next range partition.
*/
if (bi->indexes[*lb_pos + 2] < 0)
*lb_pos = *lb_pos + 2;
else
*lb_pos = *lb_pos + 1;
}
return ub->index;
}
/*
* compare_range_partitions
* Compare the bounds of two range partitions, and return true if the
* two partitions overlap, false otherwise
*
* *lb_cmpval is set to -1, 0, or 1 if the outer partition's lower bound is
* lower than, equal to, or higher than the inner partition's lower bound
* respectively. Likewise, *ub_cmpval is set to -1, 0, or 1 if the outer
* partition's upper bound is lower than, equal to, or higher than the inner
* partition's upper bound respectively.
*/
static bool
compare_range_partitions(int partnatts, FmgrInfo *partsupfuncs,
Oid *partcollations,
PartitionRangeBound *outer_lb,
PartitionRangeBound *outer_ub,
PartitionRangeBound *inner_lb,
PartitionRangeBound *inner_ub,
int *lb_cmpval, int *ub_cmpval)
{
/*
* Check if the outer partition's upper bound is lower than the inner
* partition's lower bound; if so the partitions aren't overlapping.
*/
if (compare_range_bounds(partnatts, partsupfuncs, partcollations,
outer_ub, inner_lb) < 0)
{
*lb_cmpval = -1;
*ub_cmpval = -1;
return false;
}
/*
* Check if the outer partition's lower bound is higher than the inner
* partition's upper bound; if so the partitions aren't overlapping.
*/
if (compare_range_bounds(partnatts, partsupfuncs, partcollations,
outer_lb, inner_ub) > 0)
{
*lb_cmpval = 1;
*ub_cmpval = 1;
return false;
}
/*
* get_merged_range_bounds
* Given the bounds of range partitions to be joined, determine the bounds
* of a merged partition produced from the range partitions
*
* *merged_lb and *merged_ub are set to the lower and upper bounds of the
* merged partition.
*/
static void
get_merged_range_bounds(int partnatts, FmgrInfo *partsupfuncs,
Oid *partcollations, JoinType jointype,
PartitionRangeBound *outer_lb,
PartitionRangeBound *outer_ub,
PartitionRangeBound *inner_lb,
PartitionRangeBound *inner_ub,
int lb_cmpval, int ub_cmpval,
PartitionRangeBound *merged_lb,
PartitionRangeBound *merged_ub)
{
Assert(compare_range_bounds(partnatts, partsupfuncs, partcollations,
outer_lb, inner_lb) == lb_cmpval);
Assert(compare_range_bounds(partnatts, partsupfuncs, partcollations,
outer_ub, inner_ub) == ub_cmpval);
switch (jointype)
{
case JOIN_INNER:
case JOIN_SEMI:
/*
* An INNER/SEMI join will have the rows that fit both sides, so
* the lower bound of the merged partition will be the higher of
* the two lower bounds, and the upper bound of the merged
* partition will be the lower of the two upper bounds.
*/
*merged_lb = (lb_cmpval > 0) ? *outer_lb : *inner_lb;
*merged_ub = (ub_cmpval < 0) ? *outer_ub : *inner_ub;
break;
case JOIN_LEFT:
case JOIN_ANTI:
/*
* A LEFT/ANTI join will have all the rows from the outer side, so
* the bounds of the merged partition will be the same as the
* outer bounds.
*/
*merged_lb = *outer_lb;
*merged_ub = *outer_ub;
break;
case JOIN_FULL:
/*
* A FULL join will have all the rows from both sides, so the
* lower bound of the merged partition will be the lower of the
* two lower bounds, and the upper bound of the merged partition
* will be the higher of the two upper bounds.
*/
*merged_lb = (lb_cmpval < 0) ? *outer_lb : *inner_lb;
*merged_ub = (ub_cmpval > 0) ? *outer_ub : *inner_ub;
break;
/*
* add_merged_range_bounds
* Add the bounds of a merged partition to the lists of range bounds
*/
static void
add_merged_range_bounds(int partnatts, FmgrInfo *partsupfuncs,
Oid *partcollations,
PartitionRangeBound *merged_lb,
PartitionRangeBound *merged_ub,
int merged_index,
List **merged_datums,
List **merged_kinds,
List **merged_indexes)
{
int cmpval;
if (!*merged_datums)
{
/* First merged partition */
Assert(!*merged_kinds);
Assert(!*merged_indexes);
cmpval = 1;
}
else
{
PartitionRangeBound prev_ub;
/* Get the last upper bound. */
prev_ub.index = llast_int(*merged_indexes);
prev_ub.datums = (Datum *) llast(*merged_datums);
prev_ub.kind = (PartitionRangeDatumKind *) llast(*merged_kinds);
prev_ub.lower = false;
/*
* We pass lower1 = false to partition_rbound_cmp() to prevent it from
* considering the last upper bound to be smaller than the lower bound
* of the merged partition when the values of the two range bounds
* compare equal.
*/
cmpval = partition_rbound_cmp(partnatts, partsupfuncs, partcollations,
merged_lb->datums, merged_lb->kind,
false, &prev_ub);
Assert(cmpval >= 0);
}
/*
* If the lower bound is higher than the last upper bound, add the lower
* bound with the index as -1 indicating that that is a lower bound; else,
* the last upper bound will be reused as the lower bound of the merged
* partition, so skip this.
*/
if (cmpval > 0)
{
*merged_datums = lappend(*merged_datums, merged_lb->datums);
*merged_kinds = lappend(*merged_kinds, merged_lb->kind);
*merged_indexes = lappend_int(*merged_indexes, -1);
}
/* Add the upper bound and index of the merged partition. */
*merged_datums = lappend(*merged_datums, merged_ub->datums);
*merged_kinds = lappend(*merged_kinds, merged_ub->kind);
*merged_indexes = lappend_int(*merged_indexes, merged_index);
}
/*
* partitions_are_ordered
* Determine whether the partitions described by 'boundinfo' are ordered,
* that is partitions appearing earlier in the PartitionDesc sequence
* contain partition keys strictly less than those appearing later.
* Also, if NULL values are possible, they must come in the last
* partition defined in the PartitionDesc. 'live_parts' marks which
* partitions we should include when checking the ordering. Partitions
* that do not appear in 'live_parts' are ignored.
*
* If out of order, or there is insufficient info to know the order,
* then we return false.
*/
bool
partitions_are_ordered(PartitionBoundInfo boundinfo, Bitmapset *live_parts)
{
Assert(boundinfo != NULL);
switch (boundinfo->strategy)
{
case PARTITION_STRATEGY_RANGE:
/*
* RANGE-type partitioning guarantees that the partitions can be
* scanned in the order that they're defined in the PartitionDesc
* to provide sequential, non-overlapping ranges of tuples.
* However, if a DEFAULT partition exists and it's contained
* within live_parts, then the partitions are not ordered.
*/
if (!partition_bound_has_default(boundinfo) ||
!bms_is_member(boundinfo->default_index, live_parts))
return true;
break;
case PARTITION_STRATEGY_LIST:
/*
* LIST partitioned are ordered providing none of live_parts
* overlap with the partitioned table's interleaved partitions.
*/
if (!bms_overlap(live_parts, boundinfo->interleaved_parts))
return true;
break;
case PARTITION_STRATEGY_HASH:
break;
}
return false;
}
/*
* check_new_partition_bound
*
* Checks if the new partition's bound overlaps any of the existing partitions
* of parent. Also performs additional checks as necessary per strategy.
*/
void
check_new_partition_bound(char *relname, Relation parent,
PartitionBoundSpec *spec, ParseState *pstate)
{
PartitionKey key = RelationGetPartitionKey(parent);
PartitionDesc partdesc = RelationGetPartitionDesc(parent, false);
PartitionBoundInfo boundinfo = partdesc->boundinfo;
int with = -1;
bool overlap = false;
int overlap_location = -1;
if (spec->is_default)
{
/*
* The default partition bound never conflicts with any other
* partition's; if that's what we're attaching, the only possible
* problem is that one already exists, so check for that and we're
* done.
*/
if (boundinfo == NULL || !partition_bound_has_default(boundinfo))
return;
if (partdesc->nparts > 0)
{
int greatest_modulus;
int remainder;
int offset;
/*
* Check rule that every modulus must be a factor of the
* next larger modulus. (For example, if you have a bunch
* of partitions that all have modulus 5, you can add a
* new partition with modulus 10 or a new partition with
* modulus 15, but you cannot add both a partition with
* modulus 10 and a partition with modulus 15, because 10
* is not a factor of 15.) We need only check the next
* smaller and next larger existing moduli, relying on
* previous enforcement of this rule to be sure that the
* rest are in line.
*/
/*
* Get the greatest (modulus, remainder) pair contained in
* boundinfo->datums that is less than or equal to the
* (spec->modulus, spec->remainder) pair.
*/
offset = partition_hash_bsearch(boundinfo,
spec->modulus,
spec->remainder);
if (offset < 0)
{
int next_modulus;
/*
* All existing moduli are greater or equal, so the
* new one must be a factor of the smallest one, which
* is first in the boundinfo.
*/
next_modulus = DatumGetInt32(boundinfo->datums[0][0]);
if (next_modulus % spec->modulus != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("every hash partition modulus must be a factor of the next larger modulus"),
errdetail("The new modulus %d is not a factor of %d, the modulus of existing partition \"%s\".",
spec->modulus, next_modulus,
get_rel_name(partdesc->oids[0]))));
}
else
{
int prev_modulus;
/*
* We found the largest (modulus, remainder) pair less
* than or equal to the new one. That modulus must be
* a divisor of, or equal to, the new modulus.
*/
prev_modulus = DatumGetInt32(boundinfo->datums[offset][0]);
if (spec->modulus % prev_modulus != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("every hash partition modulus must be a factor of the next larger modulus"),
errdetail("The new modulus %d is not divisible by %d, the modulus of existing partition \"%s\".",
spec->modulus,
prev_modulus,
get_rel_name(partdesc->oids[offset]))));
if (offset + 1 < boundinfo->ndatums)
{
int next_modulus;
/*
* Look at the next higher (modulus, remainder)
* pair. That could have the same modulus and a
* larger remainder than the new pair, in which
* case we're good. If it has a larger modulus,
* the new modulus must divide that one.
*/
next_modulus = DatumGetInt32(boundinfo->datums[offset + 1][0]);
if (next_modulus % spec->modulus != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("every hash partition modulus must be a factor of the next larger modulus"),
errdetail("The new modulus %d is not a factor of %d, the modulus of existing partition \"%s\".",
spec->modulus, next_modulus,
get_rel_name(partdesc->oids[offset + 1]))));
}
}
/*
* Normally, the lowest remainder that could conflict with
* the new partition is equal to the remainder specified
* for the new partition, but when the new partition has a
* modulus higher than any used so far, we need to adjust.
*/
if (remainder >= greatest_modulus)
remainder = remainder % greatest_modulus;
/* Check every potentially-conflicting remainder. */
do
{
if (boundinfo->indexes[remainder] != -1)
{
overlap = true;
overlap_location = spec->location;
with = boundinfo->indexes[remainder];
break;
}
remainder += spec->modulus;
} while (remainder < greatest_modulus);
}
break;
}
case PARTITION_STRATEGY_LIST:
{
Assert(spec->strategy == PARTITION_STRATEGY_LIST);
/*
* First check if the resulting range would be empty with
* specified lower and upper bounds. partition_rbound_cmp
* cannot return zero here, since the lower-bound flags are
* different.
*/
cmpval = partition_rbound_cmp(key->partnatts,
key->partsupfunc,
key->partcollation,
lower->datums, lower->kind,
true, upper);
Assert(cmpval != 0);
if (cmpval > 0)
{
/* Point to problematic key in the lower datums list. */
PartitionRangeDatum *datum = list_nth(spec->lowerdatums,
cmpval - 1);
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("empty range bound specified for partition \"%s\"",
relname),
errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
get_range_partbound_string(spec->lowerdatums),
get_range_partbound_string(spec->upperdatums)),
parser_errposition(pstate, datum->location)));
}
/*
* Test whether the new lower bound (which is treated
* inclusively as part of the new partition) lies inside
* an existing partition, or in a gap.
*
* If it's inside an existing partition, the bound at
* offset + 1 will be the upper bound of that partition,
* and its index will be >= 0.
*
* If it's in a gap, the bound at offset + 1 will be the
* lower bound of the next partition, and its index will
* be -1. This is also true if there is no next partition,
* since the index array is initialised with an extra -1
* at the end.
*/
offset = partition_range_bsearch(key->partnatts,
key->partsupfunc,
key->partcollation,
boundinfo, lower,
&cmpval);
if (boundinfo->indexes[offset + 1] < 0)
{
/*
* Check that the new partition will fit in the gap.
* For it to fit, the new upper bound must be less
* than or equal to the lower bound of the next
* partition, if there is one.
*/
if (offset + 1 < boundinfo->ndatums)
{
Datum *datums;
PartitionRangeDatumKind *kind;
bool is_lower;
cmpval = partition_rbound_cmp(key->partnatts,
key->partsupfunc,
key->partcollation,
datums, kind,
is_lower, upper);
if (cmpval < 0)
{
/*
* Point to problematic key in the upper
* datums list.
*/
PartitionRangeDatum *datum =
list_nth(spec->upperdatums, abs(cmpval) - 1);
/*
* The new partition overlaps with the
* existing partition between offset + 1 and
* offset + 2.
*/
overlap = true;
overlap_location = datum->location;
with = boundinfo->indexes[offset + 2];
}
}
}
else
{
/*
* The new partition overlaps with the existing
* partition between offset and offset + 1.
*/
PartitionRangeDatum *datum;
/*
* Point to problematic key in the lower datums list;
* if we have equality, point to the first one.
*/
datum = cmpval == 0 ? linitial(spec->lowerdatums) :
list_nth(spec->lowerdatums, abs(cmpval) - 1);
overlap = true;
overlap_location = datum->location;
with = boundinfo->indexes[offset + 1];
}
}
break;
}
}
if (overlap)
{
Assert(with >= 0);
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("partition \"%s\" would overlap partition \"%s\"",
relname, get_rel_name(partdesc->oids[with])),
parser_errposition(pstate, overlap_location)));
}
}
/*
* check_default_partition_contents
*
* This function checks if there exists a row in the default partition that
* would properly belong to the new partition being added. If it finds one,
* it throws an error.
*/
void
check_default_partition_contents(Relation parent, Relation default_rel,
PartitionBoundSpec *new_spec)
{
List *new_part_constraints;
List *def_part_constraints;
List *all_parts;
ListCell *lc;
/*
* Map the Vars in the constraint expression from parent's attnos to
* default_rel's.
*/
def_part_constraints =
map_partition_varattnos(def_part_constraints, 1, default_rel,
parent);
/*
* If the existing constraints on the default partition imply that it will
* not contain any row that would belong to the new partition, we can
* avoid scanning the default partition.
*/
if (PartConstraintImpliedByRelConstraint(default_rel, def_part_constraints))
{
ereport(DEBUG1,
(errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
RelationGetRelationName(default_rel))));
return;
}
/*
* Scan the default partition and its subpartitions, and check for rows
* that do not satisfy the revised partition constraints.
*/
if (default_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
all_parts = find_all_inheritors(RelationGetRelid(default_rel),
AccessExclusiveLock, NULL);
else
all_parts = list_make1_oid(RelationGetRelid(default_rel));
/* Lock already taken above. */
if (part_relid != RelationGetRelid(default_rel))
{
part_rel = table_open(part_relid, NoLock);
/*
* Map the Vars in the constraint expression from default_rel's
* the sub-partition's.
*/
partition_constraint = make_ands_explicit(def_part_constraints);
partition_constraint = (Expr *)
map_partition_varattnos((List *) partition_constraint, 1,
part_rel, default_rel);
/*
* If the partition constraints on default partition child imply
* that it will not contain any row that would belong to the new
* partition, we can avoid scanning the child table.
*/
if (PartConstraintImpliedByRelConstraint(part_rel,
def_part_constraints))
{
ereport(DEBUG1,
(errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
RelationGetRelationName(part_rel))));
/*
* Only RELKIND_RELATION relations (i.e. leaf partitions) need to be
* scanned.
*/
if (part_rel->rd_rel->relkind != RELKIND_RELATION)
{
if (part_rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
ereport(WARNING,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("skipped scanning foreign table \"%s\" which is a partition of default partition \"%s\"",
RelationGetRelationName(part_rel),
RelationGetRelationName(default_rel))));
if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
table_close(part_rel, NoLock);
continue;
}
estate = CreateExecutorState();
/* Build expression execution states for partition check quals */
partqualstate = ExecPrepareExpr(partition_constraint, estate);
while (table_scan_getnextslot(scan, ForwardScanDirection, tupslot))
{
econtext->ecxt_scantuple = tupslot;
if (!ExecCheck(partqualstate, econtext))
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
RelationGetRelationName(default_rel)),
errtable(default_rel)));
/* *partition_rbound_datum_cmp * *Returnwhetherrangebound(specifiedinrb_datumsandrb_kind) *is<,=,or>partitionkeyoftuple(tuple_datums) * *n_tuple_datums,partsupfuncandpartcollationgivenumberofattributesin *theboundstobecompared,comparisonfunctiontobeusedandthecollations *ofattributesresp.
*/
int32
partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation,
Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
Datum *tuple_datums, int n_tuple_datums)
{ int i;
int32 cmpval = -1;
for (i = 0; i < n_tuple_datums; i++)
{ if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE) return -1; elseif (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE) return1;
lo = -1;
hi = boundinfo->ndatums - 1; while (lo < hi)
{
mid = (lo + hi + 1) / 2;
*cmpval = partition_rbound_cmp(partnatts, partsupfunc,
partcollation,
boundinfo->datums[mid],
boundinfo->kind[mid],
(boundinfo->indexes[mid] == -1),
probe); if (*cmpval <= 0)
{
lo = mid; if (*cmpval == 0) break;
} else
hi = mid - 1;
}
return lo;
}
/* *partition_range_datum_bsearch *Returnstheindexofthegreatestrangeboundthatislessthanor *equaltothegiventupleor-1ifalloftherangeboundsaregreater * **is_equalissettotrueiftherangeboundatthereturnedindexisequal *totheinputtuple.
*/ int
partition_range_datum_bsearch(FmgrInfo *partsupfunc, Oid *partcollation,
PartitionBoundInfo boundinfo, int nvalues, Datum *values, bool *is_equal)
{ int lo,
hi,
mid;
lo = -1;
hi = boundinfo->ndatums - 1; while (lo < hi)
{
int32 cmpval;
mid = (lo + hi + 1) / 2;
cmpval = partition_rbound_datum_cmp(partsupfunc,
partcollation,
boundinfo->datums[mid],
boundinfo->kind[mid],
values,
nvalues); if (cmpval <= 0)
{
lo = mid;
*is_equal = (cmpval == 0);
if (*is_equal) break;
} else
hi = mid - 1;
}
return lo;
}
/* *partition_hash_bsearch *Returnstheindexofthegreatest(modulus,remainder)pairthatis *lessthanorequaltothegiven(modulus,remainder)pairor-1if *allofthemaregreater
*/ int
partition_hash_bsearch(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;
/* Generate the actual expression */ switch (key->strategy)
{ case PARTITION_STRATEGY_LIST:
{
List *elems = (List *) arg2; int nelems = list_length(elems);
/* *Finally,thedefaultpartitioncontainseverything*NOT* *containedinthenon-defaultpartitions.
*/
result = list_make1(makeBoolExpr(NOT_EXPR,
list_make1(other_parts_constr), -1));
}
return result;
}
/* *Ifitistherecursivecallfordefault,weskiptheget_range_nulltest *toavoidaccumulatingtheNullTestonthesamekeysforeachpartition.
*/ if (!for_default)
result = get_range_nulltest(key);
/* If not equal, go generate the OR expressions */ if (!DatumGetBool(test_result)) break;
/* *Theboundsforthelastkeycolumncan'tbeequal,becausesucha *rangepartitionwouldneverbeallowedtobedefined(itwouldhave *anemptyrangeotherwise).
*/ if (i == key->partnatts - 1)
elog(ERROR, "invalid range bound specification");
/* Equal, so generate keyCol = lower_val expression */
result = lappend(result,
make_partition_op_expr(key, i, BTEqualStrategyNumber,
keyCol, (Expr *) lower_val));
i++;
}
/* First pair of lower_val and upper_val that are not equal. */
lower_or_start_datum = cell1;
upper_or_start_datum = cell2;
/* OR will have as many arms as there are key columns left. */
num_or_arms = key->partnatts - i;
current_or_arm = 0;
lower_or_arms = upper_or_arms = NIL;
need_next_lower_arm = need_next_upper_arm = true; while (current_or_arm < num_or_arms)
{
List *lower_or_arm_args = NIL,
*upper_or_arm_args = NIL;
/* Restart scan of columns from the i'th one */
j = i;
partexprs_item = partexprs_item_saved;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.