/* 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);
/* 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);
/* 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)
{ /* *Unlikethecaseoflistpartitioning,wewouldn'thavere-merged *partitions,sodid_remappingshouldbeleftalone.
*/
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 *InitializeaPartitionMapstructforgivenrelation
*/ staticvoid
init_partition_map(RelOptInfo *rel, PartitionMap *map)
{ int nparts = rel->nparts; int i;
/* 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 *Trytoassigngiveninnerpartitionamergedpartition,andreturnthe *indexofthemergedpartitionifsuccessful,-1otherwise * *Ifthepartitionisnewlycreated,*next_indexisincremented.Also,ifit *isthedefaultpartitionofthejoinrelation,*default_indexissettothe *indexifnotalreadydone.
*/ 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)
{ int merged_index = -1;
/* 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 *MergetheNULLpartitionsfromajoin'souterandinnersides. * *IfthemergedpartitionproducedfromthemistheNULLpartitionofthejoin *relation,*null_indexissettotheindexofthemergedpartition. * *Note:Weassumeherethatthejoinclauseforapartitionedjoinisstrict *becausehave_partkey_equi_join()requiresthatthecorrespondingoperator *bemergejoinable,andwecurrentlyassumethatmergejoinableoperatorsare *strict(seeMJEvalOuterValues()/MJEvalInnerValues()).
*/ 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)
{ bool consider_outer_null = false; bool consider_inner_null = false;
/* *merge_partition_with_dummy *Assigngivenpartitionanewpartitionofajoinrelation * *Note:Thecallerassumesthatthegivenpartitiondoesn'thaveanon-dummy *matchingpartitionontheotherside,butifthegivenpartitionfindsthe *matchingpartitionlater,wewilladjusttheassignment.
*/ staticint
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 *Adjustmergedindexesofre-mergedpartitions
*/ staticvoid
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 *Generateapairoflistsofpartitionsthatproducemergedpartitions * *Thelistsofpartitionsarebuiltintheorderofmergedpartitionindexes, *andreturnedin*outer_partsand*inner_parts.
*/ 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)
{ 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_merged_partition_bounds *CreateaPartitionBoundInfostructfrommergedpartitionbounds
*/ 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;
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;
}
staticint
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);
/* *add_merged_range_bounds *Addtheboundsofamergedpartitiontothelistsofrangebounds
*/ 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)
{ int cmpval;
if (!*merged_datums)
{ /* First merged partition */
Assert(!*merged_kinds);
Assert(!*merged_indexes);
cmpval = 1;
} else
{
PartitionRangeBound prev_ub;
/* 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);
}
/* *Getthegreatest(modulus,remainder)paircontainedin *boundinfo->datumsthatislessthanorequaltothe *(spec->modulus,spec->remainder)pair.
*/
offset = partition_hash_bsearch(boundinfo,
spec->modulus,
spec->remainder); if (offset < 0)
{ int next_modulus;
/* *Allexistingmoduliaregreaterorequal,sothe *newonemustbeafactorofthesmallestone,which *isfirstintheboundinfo.
*/
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;
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;
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]))));
}
}
/* *Firstcheckiftheresultingrangewouldbeemptywith *specifiedlowerandupperbounds.partition_rbound_cmp *cannotreturnzerohere,sincethelower-boundflagsare *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)));
}
/* *OnlyRELKIND_RELATIONrelations(i.e.leafpartitions)needtobe *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;
/* Form a single 64-bit hash value */
rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
}
}
return rowHash;
}
/* *satisfies_hash_partition * *ThisisanSQL-callablefunctionforuseinhashpartitionconstraints. *ThefirstthreeargumentsaretheparenttableOID,modulus,andremainder. *Theremainingargumentsarethevalueofthepartitioningcolumns(or *expressions);thesearehashedandtheresultsarecombinedintoasingle *hashvaluebycallinghash_combine64. * *Returnstrueifremainderproducedwhenthiscomputedsinglehashvalueis *dividedbythegivenmodulusisequaltogivenremainder,otherwisefalse. *NB:it'simportantthatthisneverreturnnull,astheconstraintmachinery *wouldconsiderthattobea"pass". * *Seeget_qual_for_hash()forusage.
*/
Datum
satisfies_hash_partition(PG_FUNCTION_ARGS)
{ typedefstruct ColumnsHashData
{
Oid relid; int nkeys;
Oid variadic_type;
int16 variadic_typlen; bool variadic_typbyval; char variadic_typalign;
Oid partcollid[PARTITION_MAX_KEYS];
FmgrInfo partsupfunc[FLEXIBLE_ARRAY_MEMBER];
} ColumnsHashData;
Oid parentId; int modulus; int remainder;
Datum seed = UInt64GetDatum(HASH_PARTITION_SEED);
ColumnsHashData *my_extra;
uint64 rowHash = 0;
/* Return false if the parent OID, modulus, or remainder is NULL. */ if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2))
PG_RETURN_BOOL(false);
parentId = PG_GETARG_OID(0);
modulus = PG_GETARG_INT32(1);
remainder = PG_GETARG_INT32(2);
/* Sanity check modulus and remainder. */ if (modulus <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("modulus for hash partition must be an integer value greater than zero"))); if (remainder < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("remainder for hash partition must be an integer value greater than or equal to zero"))); if (remainder >= modulus)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("remainder for hash partition must be less than modulus")));
/* Open parent relation and fetch partition key info */
parent = relation_open(parentId, AccessShareLock);
key = RelationGetPartitionKey(parent);
/* Reject parent table that is not hash-partitioned. */ if (key == NULL || key->strategy != PARTITION_STRATEGY_HASH)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("\"%s\" is not a hash partitioned table",
get_rel_name(parentId))));
if (!get_fn_expr_variadic(fcinfo->flinfo))
{ int nargs = PG_NARGS() - 3;
/* complain if wrong number of column values */ if (key->partnatts != nargs)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
key->partnatts, nargs)));
/* check argument types and save fmgr_infos */ for (j = 0; j < key->partnatts; ++j)
{
Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, j + 3);
if (argtype != key->parttypid[j] && !IsBinaryCoercible(argtype, key->parttypid[j]))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("column %d of the partition key has type %s, but supplied value is of type %s",
j + 1, format_type_be(key->parttypid[j]), format_type_be(argtype))));
/* allocate space for our cache -- just one FmgrInfo in this case */
fcinfo->flinfo->fn_extra =
MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
offsetof(ColumnsHashData, partsupfunc) + sizeof(FmgrInfo));
my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
my_extra->relid = parentId;
my_extra->nkeys = key->partnatts;
my_extra->variadic_type = ARR_ELEMTYPE(variadic_array);
get_typlenbyvalalign(my_extra->variadic_type,
&my_extra->variadic_typlen,
&my_extra->variadic_typbyval,
&my_extra->variadic_typalign);
my_extra->partcollid[0] = key->partcollation[0];
/* check argument types */ for (j = 0; j < key->partnatts; ++j) if (key->parttypid[j] != my_extra->variadic_type)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("column %d of the partition key has type \"%s\", but supplied value is of type \"%s\"",
j + 1,
format_type_be(key->parttypid[j]),
format_type_be(my_extra->variadic_type))));
/* Form a single 64-bit hash value */
rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
}
} else
{
ArrayType *variadic_array = PG_GETARG_ARRAYTYPE_P(3); int i; int nelems;
Datum *datum; bool *isnull;
/* complain if wrong number of column values */ if (nelems != my_extra->nkeys)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
my_extra->nkeys, nelems)));
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.