PostgreSQL Source Code git master
partbounds.h File Reference
#include "fmgr.h"
#include "parser/parse_node.h"
#include "partitioning/partdefs.h"
Include dependency graph for partbounds.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  PartitionBoundInfoData
 

Macros

#define partition_bound_accepts_nulls(bi)   ((bi)->null_index != -1)
 
#define partition_bound_has_default(bi)   ((bi)->default_index != -1)
 

Typedefs

typedef struct RelOptInfo RelOptInfo
 
typedef struct PartitionBoundInfoData PartitionBoundInfoData
 

Functions

int get_hash_partition_greatest_modulus (PartitionBoundInfo bound)
 
uint64 compute_partition_hash_value (int partnatts, FmgrInfo *partsupfunc, const Oid *partcollation, const Datum *values, const bool *isnull)
 
Listget_qual_from_partbound (Relation parent, PartitionBoundSpec *spec)
 
PartitionBoundInfo partition_bounds_create (PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
 
bool partition_bounds_equal (int partnatts, int16 *parttyplen, bool *parttypbyval, PartitionBoundInfo b1, PartitionBoundInfo b2)
 
PartitionBoundInfo partition_bounds_copy (PartitionBoundInfo src, PartitionKey key)
 
PartitionBoundInfo partition_bounds_merge (int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinType jointype, List **outer_parts, List **inner_parts)
 
bool partitions_are_ordered (PartitionBoundInfo boundinfo, Bitmapset *live_parts)
 
void check_new_partition_bound (char *relname, Relation parent, PartitionBoundSpec *spec, ParseState *pstate)
 
void check_default_partition_contents (Relation parent, Relation default_rel, PartitionBoundSpec *new_spec)
 
int32 partition_rbound_datum_cmp (FmgrInfo *partsupfunc, Oid *partcollation, const Datum *rb_datums, PartitionRangeDatumKind *rb_kind, const Datum *tuple_datums, int n_tuple_datums)
 
int partition_list_bsearch (FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
 
int partition_range_datum_bsearch (FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, int nvalues, const Datum *values, bool *is_equal)
 
int partition_hash_bsearch (PartitionBoundInfo boundinfo, int modulus, int remainder)
 
void check_partitions_for_split (Relation parent, Oid splitPartOid, List *partlist, ParseState *pstate)
 
void calculate_partition_bound_for_merge (Relation parent, List *partNames, List *partOids, PartitionBoundSpec *spec, ParseState *pstate)
 

Macro Definition Documentation

◆ partition_bound_accepts_nulls

#define partition_bound_accepts_nulls (   bi)    ((bi)->null_index != -1)

Definition at line 98 of file partbounds.h.

◆ partition_bound_has_default

#define partition_bound_has_default (   bi)    ((bi)->default_index != -1)

Definition at line 99 of file partbounds.h.

Typedef Documentation

◆ PartitionBoundInfoData

◆ RelOptInfo

typedef struct RelOptInfo RelOptInfo

Definition at line 18 of file partbounds.h.

Function Documentation

◆ calculate_partition_bound_for_merge()

void calculate_partition_bound_for_merge ( Relation  parent,
List partNames,
List partOids,
PartitionBoundSpec spec,
ParseState pstate 
)

Definition at line 5098 of file partbounds.c.

5103{
5105 PartitionBoundSpec *bound;
5106
5107 Assert(!spec->is_default);
5108
5109 switch (key->strategy)
5110 {
5112 {
5113 int i;
5114 PartitionRangeBound **lower_bounds;
5115 int nparts = list_length(partOids);
5116 List *bounds = NIL;
5117
5118 lower_bounds = (PartitionRangeBound **)
5119 palloc0(nparts * sizeof(PartitionRangeBound *));
5120
5121 /*
5122 * Create an array of lower bounds and a list of
5123 * PartitionBoundSpec.
5124 */
5125 foreach_oid(partoid, partOids)
5126 {
5127 bound = get_partition_bound_spec(partoid);
5128 i = foreach_current_index(partoid);
5129
5130 lower_bounds[i] = make_one_partition_rbound(key, i, bound->lowerdatums, true);
5131 bounds = lappend(bounds, bound);
5132 }
5133
5134 /* Sort the array of lower bounds. */
5135 qsort_arg(lower_bounds, nparts, sizeof(PartitionRangeBound *),
5137
5138 /* Ranges of partitions should be adjacent. */
5139 for (i = 1; i < nparts; i++)
5140 {
5141 int index = lower_bounds[i]->index;
5142 int prev_index = lower_bounds[i - 1]->index;
5143
5145 (RangeVar *) list_nth(partNames, prev_index),
5146 (PartitionBoundSpec *) list_nth(bounds, prev_index),
5147 (RangeVar *) list_nth(partNames, index),
5148 (PartitionBoundSpec *) list_nth(bounds, index),
5149 false,
5150 true,
5151 pstate);
5152 }
5153
5154 /*
5155 * The lower bound of the first partition is the lower bound
5156 * of the merged partition.
5157 */
5158 spec->lowerdatums =
5159 ((PartitionBoundSpec *) list_nth(bounds, lower_bounds[0]->index))->lowerdatums;
5160
5161 /*
5162 * The upper bound of the last partition is the upper bound of
5163 * the merged partition.
5164 */
5165 spec->upperdatums =
5166 ((PartitionBoundSpec *) list_nth(bounds, lower_bounds[nparts - 1]->index))->upperdatums;
5167
5168 pfree(lower_bounds);
5169 list_free(bounds);
5170 break;
5171 }
5172
5174 {
5175 /* Consolidate bounds for all partitions in the list. */
5176 foreach_oid(partoid, partOids)
5177 {
5178 bound = get_partition_bound_spec(partoid);
5179 spec->listdatums = list_concat(spec->listdatums, bound->listdatums);
5180 }
5181 break;
5182 }
5183
5184 default:
5185 elog(ERROR, "unexpected partition strategy: %d",
5186 (int) key->strategy);
5187 }
5188}
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
Assert(PointerIsAligned(start, uint64))
int i
Definition: isn.c:77
List * lappend(List *list, void *datum)
Definition: list.c:339
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
void list_free(List *list)
Definition: list.c:1546
void pfree(void *pointer)
Definition: mcxt.c:1594
void * palloc0(Size size)
Definition: mcxt.c:1395
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:900
@ PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:901
static int32 qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
Definition: partbounds.c:3802
static void check_two_partitions_bounds_range(Relation parent, RangeVar *first_name, PartitionBoundSpec *first_bound, RangeVar *second_name, PartitionBoundSpec *second_bound, bool defaultPart, bool is_merge, ParseState *pstate)
Definition: partbounds.c:4997
static PartitionBoundSpec * get_partition_bound_spec(Oid partOid)
Definition: partbounds.c:5056
static PartitionRangeBound * make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
Definition: partbounds.c:3421
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:51
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define foreach_current_index(var_or_cell)
Definition: pg_list.h:403
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define foreach_oid(var, lst)
Definition: pg_list.h:471
void qsort_arg(void *base, size_t nel, size_t elsize, qsort_arg_comparator cmp, void *arg)
Definition: pg_list.h:54
Definition: type.h:96

References Assert(), check_two_partitions_bounds_range(), elog, ERROR, foreach_current_index, foreach_oid, get_partition_bound_spec(), i, PartitionRangeBound::index, PartitionBoundSpec::is_default, sort-test::key, lappend(), list_concat(), list_free(), list_length(), list_nth(), PartitionBoundSpec::listdatums, PartitionBoundSpec::lowerdatums, make_one_partition_rbound(), NIL, palloc0(), PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, pfree(), qsort_arg(), qsort_partition_rbound_cmp(), RelationGetPartitionKey(), and PartitionBoundSpec::upperdatums.

Referenced by transformPartitionCmdForMerge().

◆ check_default_partition_contents()

void check_default_partition_contents ( Relation  parent,
Relation  default_rel,
PartitionBoundSpec new_spec 
)

Definition at line 3244 of file partbounds.c.

3246{
3247 List *new_part_constraints;
3248 List *def_part_constraints;
3249 List *all_parts;
3250 ListCell *lc;
3251
3252 new_part_constraints = (new_spec->strategy == PARTITION_STRATEGY_LIST)
3253 ? get_qual_for_list(parent, new_spec)
3254 : get_qual_for_range(parent, new_spec, false);
3255 def_part_constraints =
3256 get_proposed_default_constraint(new_part_constraints);
3257
3258 /*
3259 * Map the Vars in the constraint expression from parent's attnos to
3260 * default_rel's.
3261 */
3262 def_part_constraints =
3263 map_partition_varattnos(def_part_constraints, 1, default_rel,
3264 parent);
3265
3266 /*
3267 * If the existing constraints on the default partition imply that it will
3268 * not contain any row that would belong to the new partition, we can
3269 * avoid scanning the default partition.
3270 */
3271 if (PartConstraintImpliedByRelConstraint(default_rel, def_part_constraints))
3272 {
3274 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
3275 RelationGetRelationName(default_rel))));
3276 return;
3277 }
3278
3279 /*
3280 * Scan the default partition and its subpartitions, and check for rows
3281 * that do not satisfy the revised partition constraints.
3282 */
3283 if (default_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
3284 all_parts = find_all_inheritors(RelationGetRelid(default_rel),
3285 AccessExclusiveLock, NULL);
3286 else
3287 all_parts = list_make1_oid(RelationGetRelid(default_rel));
3288
3289 foreach(lc, all_parts)
3290 {
3291 Oid part_relid = lfirst_oid(lc);
3292 Relation part_rel;
3293 Expr *partition_constraint;
3294 EState *estate;
3295 ExprState *partqualstate = NULL;
3296 Snapshot snapshot;
3297 ExprContext *econtext;
3298 TableScanDesc scan;
3299 MemoryContext oldCxt;
3300 TupleTableSlot *tupslot;
3301
3302 /* Lock already taken above. */
3303 if (part_relid != RelationGetRelid(default_rel))
3304 {
3305 part_rel = table_open(part_relid, NoLock);
3306
3307 /*
3308 * Map the Vars in the constraint expression from default_rel's
3309 * the sub-partition's.
3310 */
3311 partition_constraint = make_ands_explicit(def_part_constraints);
3312 partition_constraint = (Expr *)
3313 map_partition_varattnos((List *) partition_constraint, 1,
3314 part_rel, default_rel);
3315
3316 /*
3317 * If the partition constraints on default partition child imply
3318 * that it will not contain any row that would belong to the new
3319 * partition, we can avoid scanning the child table.
3320 */
3322 def_part_constraints))
3323 {
3325 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
3326 RelationGetRelationName(part_rel))));
3327
3328 table_close(part_rel, NoLock);
3329 continue;
3330 }
3331 }
3332 else
3333 {
3334 part_rel = default_rel;
3335 partition_constraint = make_ands_explicit(def_part_constraints);
3336 }
3337
3338 /*
3339 * Only RELKIND_RELATION relations (i.e. leaf partitions) need to be
3340 * scanned.
3341 */
3342 if (part_rel->rd_rel->relkind != RELKIND_RELATION)
3343 {
3344 if (part_rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
3346 (errcode(ERRCODE_CHECK_VIOLATION),
3347 errmsg("skipped scanning foreign table \"%s\" which is a partition of default partition \"%s\"",
3348 RelationGetRelationName(part_rel),
3349 RelationGetRelationName(default_rel))));
3350
3351 if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
3352 table_close(part_rel, NoLock);
3353
3354 continue;
3355 }
3356
3357 estate = CreateExecutorState();
3358
3359 /* Build expression execution states for partition check quals */
3360 partqualstate = ExecPrepareExpr(partition_constraint, estate);
3361
3362 econtext = GetPerTupleExprContext(estate);
3363 snapshot = RegisterSnapshot(GetLatestSnapshot());
3364 tupslot = table_slot_create(part_rel, &estate->es_tupleTable);
3365 scan = table_beginscan(part_rel, snapshot, 0, NULL);
3366
3367 /*
3368 * Switch to per-tuple memory context and reset it for each tuple
3369 * produced, so we don't leak memory.
3370 */
3372
3373 while (table_scan_getnextslot(scan, ForwardScanDirection, tupslot))
3374 {
3375 econtext->ecxt_scantuple = tupslot;
3376
3377 if (!ExecCheck(partqualstate, econtext))
3378 ereport(ERROR,
3379 (errcode(ERRCODE_CHECK_VIOLATION),
3380 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
3381 RelationGetRelationName(default_rel)),
3382 errtable(default_rel)));
3383
3384 ResetExprContext(econtext);
3386 }
3387
3388 MemoryContextSwitchTo(oldCxt);
3389 table_endscan(scan);
3390 UnregisterSnapshot(snapshot);
3392 FreeExecutorState(estate);
3393
3394 if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
3395 table_close(part_rel, NoLock); /* keep the lock until commit */
3396 }
3397}
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1170
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#define WARNING
Definition: elog.h:36
#define DEBUG1
Definition: elog.h:30
#define ereport(elevel,...)
Definition: elog.h:150
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:765
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition: execExpr.c:872
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1443
void FreeExecutorState(EState *estate)
Definition: execUtils.c:192
EState * CreateExecutorState(void)
Definition: execUtils.c:88
#define GetPerTupleExprContext(estate)
Definition: executor.h:656
#define ResetExprContext(econtext)
Definition: executor.h:650
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:661
#define NoLock
Definition: lockdefs.h:34
#define AccessExclusiveLock
Definition: lockdefs.h:43
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:799
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:123
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
static List * get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:4058
static List * get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default)
Definition: partbounds.c:4267
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition: partition.c:222
List * get_proposed_default_constraint(List *new_part_constraints)
Definition: partition.c:370
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:255
#define list_make1_oid(x1)
Definition: pg_list.h:242
#define lfirst_oid(lc)
Definition: pg_list.h:174
unsigned int Oid
Definition: postgres_ext.h:32
#define RelationGetRelid(relation)
Definition: rel.h:515
#define RelationGetRelationName(relation)
Definition: rel.h:549
int errtable(Relation rel)
Definition: relcache.c:6049
@ ForwardScanDirection
Definition: sdir.h:28
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:354
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:866
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:824
List * es_tupleTable
Definition: execnodes.h:712
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:273
Form_pg_class rd_rel
Definition: rel.h:111
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:92
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:985
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:1020
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, ScanKeyData *key)
Definition: tableam.h:876
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:20085

References AccessExclusiveLock, CHECK_FOR_INTERRUPTS, CreateExecutorState(), DEBUG1, ExprContext::ecxt_scantuple, ereport, errcode(), errmsg(), errmsg_internal(), ERROR, errtable(), EState::es_tupleTable, ExecCheck(), ExecDropSingleTupleTableSlot(), ExecPrepareExpr(), find_all_inheritors(), ForwardScanDirection, FreeExecutorState(), get_proposed_default_constraint(), get_qual_for_list(), get_qual_for_range(), GetLatestSnapshot(), GetPerTupleExprContext, GetPerTupleMemoryContext, lfirst_oid, list_make1_oid, make_ands_explicit(), map_partition_varattnos(), MemoryContextSwitchTo(), NoLock, PartConstraintImpliedByRelConstraint(), PARTITION_STRATEGY_LIST, RelationData::rd_rel, RegisterSnapshot(), RelationGetRelationName, RelationGetRelid, ResetExprContext, PartitionBoundSpec::strategy, table_beginscan(), table_close(), table_endscan(), table_open(), table_scan_getnextslot(), table_slot_create(), UnregisterSnapshot(), and WARNING.

Referenced by DefineRelation().

◆ check_new_partition_bound()

void check_new_partition_bound ( char *  relname,
Relation  parent,
PartitionBoundSpec spec,
ParseState pstate 
)

Definition at line 2889 of file partbounds.c.

2891{
2893 PartitionDesc partdesc = RelationGetPartitionDesc(parent, false);
2894 PartitionBoundInfo boundinfo = partdesc->boundinfo;
2895 int with = -1;
2896 bool overlap = false;
2897 int overlap_location = -1;
2898
2899 if (spec->is_default)
2900 {
2901 /*
2902 * The default partition bound never conflicts with any other
2903 * partition's; if that's what we're attaching, the only possible
2904 * problem is that one already exists, so check for that and we're
2905 * done.
2906 */
2907 if (boundinfo == NULL || !partition_bound_has_default(boundinfo))
2908 return;
2909
2910 /* Default partition already exists, error out. */
2911 ereport(ERROR,
2912 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2913 errmsg("partition \"%s\" conflicts with existing default partition \"%s\"",
2914 relname, get_rel_name(partdesc->oids[boundinfo->default_index])),
2915 parser_errposition(pstate, spec->location)));
2916 }
2917
2918 switch (key->strategy)
2919 {
2921 {
2923 Assert(spec->remainder >= 0 && spec->remainder < spec->modulus);
2924
2925 if (partdesc->nparts > 0)
2926 {
2927 int greatest_modulus;
2928 int remainder;
2929 int offset;
2930
2931 /*
2932 * Check rule that every modulus must be a factor of the
2933 * next larger modulus. (For example, if you have a bunch
2934 * of partitions that all have modulus 5, you can add a
2935 * new partition with modulus 10 or a new partition with
2936 * modulus 15, but you cannot add both a partition with
2937 * modulus 10 and a partition with modulus 15, because 10
2938 * is not a factor of 15.) We need only check the next
2939 * smaller and next larger existing moduli, relying on
2940 * previous enforcement of this rule to be sure that the
2941 * rest are in line.
2942 */
2943
2944 /*
2945 * Get the greatest (modulus, remainder) pair contained in
2946 * boundinfo->datums that is less than or equal to the
2947 * (spec->modulus, spec->remainder) pair.
2948 */
2949 offset = partition_hash_bsearch(boundinfo,
2950 spec->modulus,
2951 spec->remainder);
2952 if (offset < 0)
2953 {
2954 int next_modulus;
2955
2956 /*
2957 * All existing moduli are greater or equal, so the
2958 * new one must be a factor of the smallest one, which
2959 * is first in the boundinfo.
2960 */
2961 next_modulus = DatumGetInt32(boundinfo->datums[0][0]);
2962 if (next_modulus % spec->modulus != 0)
2963 ereport(ERROR,
2964 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2965 errmsg("every hash partition modulus must be a factor of the next larger modulus"),
2966 errdetail("The new modulus %d is not a factor of %d, the modulus of existing partition \"%s\".",
2967 spec->modulus, next_modulus,
2968 get_rel_name(partdesc->oids[0]))));
2969 }
2970 else
2971 {
2972 int prev_modulus;
2973
2974 /*
2975 * We found the largest (modulus, remainder) pair less
2976 * than or equal to the new one. That modulus must be
2977 * a divisor of, or equal to, the new modulus.
2978 */
2979 prev_modulus = DatumGetInt32(boundinfo->datums[offset][0]);
2980
2981 if (spec->modulus % prev_modulus != 0)
2982 ereport(ERROR,
2983 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2984 errmsg("every hash partition modulus must be a factor of the next larger modulus"),
2985 errdetail("The new modulus %d is not divisible by %d, the modulus of existing partition \"%s\".",
2986 spec->modulus,
2987 prev_modulus,
2988 get_rel_name(partdesc->oids[offset]))));
2989
2990 if (offset + 1 < boundinfo->ndatums)
2991 {
2992 int next_modulus;
2993
2994 /*
2995 * Look at the next higher (modulus, remainder)
2996 * pair. That could have the same modulus and a
2997 * larger remainder than the new pair, in which
2998 * case we're good. If it has a larger modulus,
2999 * the new modulus must divide that one.
3000 */
3001 next_modulus = DatumGetInt32(boundinfo->datums[offset + 1][0]);
3002
3003 if (next_modulus % spec->modulus != 0)
3004 ereport(ERROR,
3005 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3006 errmsg("every hash partition modulus must be a factor of the next larger modulus"),
3007 errdetail("The new modulus %d is not a factor of %d, the modulus of existing partition \"%s\".",
3008 spec->modulus, next_modulus,
3009 get_rel_name(partdesc->oids[offset + 1]))));
3010 }
3011 }
3012
3013 greatest_modulus = boundinfo->nindexes;
3014 remainder = spec->remainder;
3015
3016 /*
3017 * Normally, the lowest remainder that could conflict with
3018 * the new partition is equal to the remainder specified
3019 * for the new partition, but when the new partition has a
3020 * modulus higher than any used so far, we need to adjust.
3021 */
3022 if (remainder >= greatest_modulus)
3023 remainder = remainder % greatest_modulus;
3024
3025 /* Check every potentially-conflicting remainder. */
3026 do
3027 {
3028 if (boundinfo->indexes[remainder] != -1)
3029 {
3030 overlap = true;
3031 overlap_location = spec->location;
3032 with = boundinfo->indexes[remainder];
3033 break;
3034 }
3035 remainder += spec->modulus;
3036 } while (remainder < greatest_modulus);
3037 }
3038
3039 break;
3040 }
3041
3043 {
3045
3046 if (partdesc->nparts > 0)
3047 {
3048 ListCell *cell;
3049
3050 Assert(boundinfo &&
3051 boundinfo->strategy == PARTITION_STRATEGY_LIST &&
3052 (boundinfo->ndatums > 0 ||
3053 partition_bound_accepts_nulls(boundinfo) ||
3054 partition_bound_has_default(boundinfo)));
3055
3056 foreach(cell, spec->listdatums)
3057 {
3058 Const *val = lfirst_node(Const, cell);
3059
3060 overlap_location = val->location;
3061 if (!val->constisnull)
3062 {
3063 int offset;
3064 bool equal;
3065
3066 offset = partition_list_bsearch(&key->partsupfunc[0],
3067 key->partcollation,
3068 boundinfo,
3069 val->constvalue,
3070 &equal);
3071 if (offset >= 0 && equal)
3072 {
3073 overlap = true;
3074 with = boundinfo->indexes[offset];
3075 break;
3076 }
3077 }
3078 else if (partition_bound_accepts_nulls(boundinfo))
3079 {
3080 overlap = true;
3081 with = boundinfo->null_index;
3082 break;
3083 }
3084 }
3085 }
3086
3087 break;
3088 }
3089
3091 {
3093 *upper;
3094 int cmpval;
3095
3097 lower = make_one_partition_rbound(key, -1, spec->lowerdatums, true);
3098 upper = make_one_partition_rbound(key, -1, spec->upperdatums, false);
3099
3100 /*
3101 * First check if the resulting range would be empty with
3102 * specified lower and upper bounds. partition_rbound_cmp
3103 * cannot return zero here, since the lower-bound flags are
3104 * different.
3105 */
3106 cmpval = partition_rbound_cmp(key->partnatts,
3107 key->partsupfunc,
3108 key->partcollation,
3109 lower->datums, lower->kind,
3110 true, upper);
3111 Assert(cmpval != 0);
3112 if (cmpval > 0)
3113 {
3114 /* Point to problematic key in the lower datums list. */
3116 cmpval - 1);
3117
3118 ereport(ERROR,
3119 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3120 errmsg("empty range bound specified for partition \"%s\"",
3121 relname),
3122 errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
3125 parser_errposition(pstate, datum->location)));
3126 }
3127
3128 if (partdesc->nparts > 0)
3129 {
3130 int offset;
3131
3132 Assert(boundinfo &&
3133 boundinfo->strategy == PARTITION_STRATEGY_RANGE &&
3134 (boundinfo->ndatums > 0 ||
3135 partition_bound_has_default(boundinfo)));
3136
3137 /*
3138 * Test whether the new lower bound (which is treated
3139 * inclusively as part of the new partition) lies inside
3140 * an existing partition, or in a gap.
3141 *
3142 * If it's inside an existing partition, the bound at
3143 * offset + 1 will be the upper bound of that partition,
3144 * and its index will be >= 0.
3145 *
3146 * If it's in a gap, the bound at offset + 1 will be the
3147 * lower bound of the next partition, and its index will
3148 * be -1. This is also true if there is no next partition,
3149 * since the index array is initialised with an extra -1
3150 * at the end.
3151 */
3152 offset = partition_range_bsearch(key->partnatts,
3153 key->partsupfunc,
3154 key->partcollation,
3155 boundinfo, lower,
3156 &cmpval);
3157
3158 if (boundinfo->indexes[offset + 1] < 0)
3159 {
3160 /*
3161 * Check that the new partition will fit in the gap.
3162 * For it to fit, the new upper bound must be less
3163 * than or equal to the lower bound of the next
3164 * partition, if there is one.
3165 */
3166 if (offset + 1 < boundinfo->ndatums)
3167 {
3168 Datum *datums;
3170 bool is_lower;
3171
3172 datums = boundinfo->datums[offset + 1];
3173 kind = boundinfo->kind[offset + 1];
3174 is_lower = (boundinfo->indexes[offset + 1] == -1);
3175
3176 cmpval = partition_rbound_cmp(key->partnatts,
3177 key->partsupfunc,
3178 key->partcollation,
3179 datums, kind,
3180 is_lower, upper);
3181 if (cmpval < 0)
3182 {
3183 /*
3184 * Point to problematic key in the upper
3185 * datums list.
3186 */
3187 PartitionRangeDatum *datum =
3188 list_nth(spec->upperdatums, abs(cmpval) - 1);
3189
3190 /*
3191 * The new partition overlaps with the
3192 * existing partition between offset + 1 and
3193 * offset + 2.
3194 */
3195 overlap = true;
3196 overlap_location = datum->location;
3197 with = boundinfo->indexes[offset + 2];
3198 }
3199 }
3200 }
3201 else
3202 {
3203 /*
3204 * The new partition overlaps with the existing
3205 * partition between offset and offset + 1.
3206 */
3207 PartitionRangeDatum *datum;
3208
3209 /*
3210 * Point to problematic key in the lower datums list;
3211 * if we have equality, point to the first one.
3212 */
3213 datum = cmpval == 0 ? linitial(spec->lowerdatums) :
3214 list_nth(spec->lowerdatums, abs(cmpval) - 1);
3215 overlap = true;
3216 overlap_location = datum->location;
3217 with = boundinfo->indexes[offset + 1];
3218 }
3219 }
3220
3221 break;
3222 }
3223 }
3224
3225 if (overlap)
3226 {
3227 Assert(with >= 0);
3228 ereport(ERROR,
3229 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3230 errmsg("partition \"%s\" would overlap partition \"%s\"",
3231 relname, get_rel_name(partdesc->oids[with])),
3232 parser_errposition(pstate, overlap_location)));
3233 }
3234}
int errdetail(const char *fmt,...)
Definition: elog.c:1216
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
size_t remainder
long val
Definition: informix.c:689
char * get_rel_name(Oid relid)
Definition: lsyscache.c:2093
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:49
Datum upper(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:80
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
@ PARTITION_STRATEGY_HASH
Definition: parsenodes.h:902
PartitionRangeDatumKind
Definition: parsenodes.h:951
static int partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, PartitionRangeBound *probe, int32 *cmpval)
Definition: partbounds.c:3645
static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partbounds.c:3480
int partition_hash_bsearch(PartitionBoundInfo boundinfo, int modulus, int remainder)
Definition: partbounds.c:3730
int partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
Definition: partbounds.c:3599
#define partition_bound_has_default(bi)
Definition: partbounds.h:99
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:98
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition: partdesc.c:71
NameData relname
Definition: pg_class.h:38
#define lfirst_node(type, lc)
Definition: pg_list.h:176
#define linitial(l)
Definition: pg_list.h:178
uint64_t Datum
Definition: postgres.h:70
static int32 DatumGetInt32(Datum X)
Definition: postgres.h:212
char * get_range_partbound_string(List *bound_datums)
Definition: ruleutils.c:13711
PartitionRangeDatumKind ** kind
Definition: partbounds.h:84
PartitionStrategy strategy
Definition: partbounds.h:81
PartitionBoundInfo boundinfo
Definition: partdesc.h:38

References Assert(), PartitionDescData::boundinfo, DatumGetInt32(), PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, equal(), ereport, errcode(), errdetail(), errmsg(), ERROR, get_range_partbound_string(), get_rel_name(), PartitionBoundInfoData::indexes, PartitionBoundSpec::is_default, sort-test::key, PartitionBoundInfoData::kind, lfirst_node, linitial, list_nth(), PartitionBoundSpec::listdatums, PartitionBoundSpec::location, PartitionRangeDatum::location, lower(), PartitionBoundSpec::lowerdatums, make_one_partition_rbound(), PartitionBoundSpec::modulus, PartitionBoundInfoData::ndatums, PartitionBoundInfoData::nindexes, PartitionDescData::nparts, PartitionBoundInfoData::null_index, PartitionDescData::oids, parser_errposition(), partition_bound_accepts_nulls, partition_bound_has_default, partition_hash_bsearch(), partition_list_bsearch(), partition_range_bsearch(), partition_rbound_cmp(), PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, RelationGetPartitionDesc(), RelationGetPartitionKey(), relname, remainder, PartitionBoundSpec::remainder, PartitionBoundSpec::strategy, PartitionBoundInfoData::strategy, upper(), PartitionBoundSpec::upperdatums, and val.

Referenced by ATExecAttachPartition(), check_partitions_for_split(), and DefineRelation().

◆ check_partitions_for_split()

void check_partitions_for_split ( Relation  parent,
Oid  splitPartOid,
List partlist,
ParseState pstate 
)

Definition at line 5724 of file partbounds.c.

5728{
5730 char strategy;
5731 Oid defaultPartOid;
5732 bool isSplitPartDefault;
5733 bool createDefaultPart = false;
5734 int default_index = -1;
5735 int i;
5736 SinglePartitionSpec **new_parts;
5737 SinglePartitionSpec *spsPrev = NULL;
5738
5739 /*
5740 * nparts counts the number of split partitions, but it exclude the
5741 * default partition.
5742 */
5743 int nparts = 0;
5744
5745 key = RelationGetPartitionKey(parent);
5746 strategy = get_partition_strategy(key);
5747
5748 defaultPartOid =
5750
5751 Assert(strategy == PARTITION_STRATEGY_RANGE ||
5752 strategy == PARTITION_STRATEGY_LIST);
5753
5754 /*
5755 * Make an array new_parts with new partitions except the DEFAULT
5756 * partition.
5757 */
5758 new_parts = (SinglePartitionSpec **)
5759 palloc0(list_length(partlist) * sizeof(SinglePartitionSpec *));
5760
5761 /* isSplitPartDefault flag: is split partition a DEFAULT partition? */
5762 isSplitPartDefault = (defaultPartOid == splitPartOid);
5763
5764 foreach_node(SinglePartitionSpec, sps, partlist)
5765 {
5766 if (sps->bound->is_default)
5767 default_index = foreach_current_index(sps);
5768 else
5769 new_parts[nparts++] = sps;
5770 }
5771
5772 /* An indicator that the DEFAULT partition will be created. */
5773 if (default_index != -1)
5774 {
5775 createDefaultPart = true;
5776 Assert(nparts == list_length(partlist) - 1);
5777 }
5778
5779 if (strategy == PARTITION_STRATEGY_RANGE)
5780 {
5781 PartitionRangeBound **lower_bounds;
5782 SinglePartitionSpec **tmp_new_parts;
5783
5784 /*
5785 * To simplify the check for ranges of new partitions, we need to sort
5786 * all partitions in ascending order of their bounds (we compare the
5787 * lower bound only).
5788 */
5789 lower_bounds = (PartitionRangeBound **)
5790 palloc0(nparts * sizeof(PartitionRangeBound *));
5791
5792 /* Create an array of lower bounds. */
5793 for (i = 0; i < nparts; i++)
5794 {
5795 lower_bounds[i] = make_one_partition_rbound(key, i,
5796 new_parts[i]->bound->lowerdatums, true);
5797 }
5798
5799 /* Sort the array of lower bounds. */
5800 qsort_arg(lower_bounds, nparts, sizeof(PartitionRangeBound *),
5802
5803 /* Reorder the array of partitions. */
5804 tmp_new_parts = new_parts;
5805 new_parts = (SinglePartitionSpec **)
5806 palloc0(nparts * sizeof(SinglePartitionSpec *));
5807 for (i = 0; i < nparts; i++)
5808 new_parts[i] = tmp_new_parts[lower_bounds[i]->index];
5809
5810 pfree(tmp_new_parts);
5811 pfree(lower_bounds);
5812 }
5813
5814 for (i = 0; i < nparts; i++)
5815 {
5816 SinglePartitionSpec *sps = new_parts[i];
5817
5818 if (isSplitPartDefault)
5819 {
5820 /*
5821 * When the split partition is the DEFAULT partition, we can use
5822 * any free ranges - as when creating a new partition.
5823 */
5824 check_new_partition_bound(sps->name->relname, parent, sps->bound,
5825 pstate);
5826 }
5827 else
5828 {
5829 /*
5830 * Checks that the bounds of the current partition are inside the
5831 * bounds of the split partition. For range partitioning: checks
5832 * that the upper bound of the previous partition is equal to the
5833 * lower bound of the current partition. For list partitioning:
5834 * checks that the split partition contains all values of the
5835 * current partition.
5836 */
5837 if (strategy == PARTITION_STRATEGY_RANGE)
5838 {
5839 bool first = (i == 0);
5840 bool last = (i == (nparts - 1));
5841
5843 splitPartOid, first, last,
5844 createDefaultPart, pstate);
5845 }
5846 else
5848 sps->bound, splitPartOid, pstate);
5849 }
5850
5851 /* Ranges of new partitions should not overlap. */
5852 if (strategy == PARTITION_STRATEGY_RANGE && spsPrev)
5853 check_two_partitions_bounds_range(parent, spsPrev->name, spsPrev->bound,
5854 sps->name, sps->bound,
5855 createDefaultPart,
5856 false,
5857 pstate);
5858
5859 spsPrev = sps;
5860 }
5861
5862 if (strategy == PARTITION_STRATEGY_LIST)
5863 {
5864 /* Values of new partitions should not overlap. */
5865 check_partitions_not_overlap_list(parent, new_parts, nparts,
5866 pstate);
5867
5868 /*
5869 * Need to check that all values of the split partition are contained
5870 * in the new partitions. Skip this check if the DEFAULT partition
5871 * exists.
5872 */
5873 if (!createDefaultPart)
5874 check_parent_values_in_new_partitions(parent, splitPartOid,
5875 new_parts, nparts, pstate);
5876 }
5877
5878 pfree(new_parts);
5879}
static void check_partition_bounds_for_split_list(Relation parent, char *relname, PartitionBoundSpec *spec, Oid splitPartOid, ParseState *pstate)
Definition: partbounds.c:5482
static void check_parent_values_in_new_partitions(Relation parent, Oid partOid, SinglePartitionSpec **parts, int nparts, ParseState *pstate)
Definition: partbounds.c:5623
void check_new_partition_bound(char *relname, Relation parent, PartitionBoundSpec *spec, ParseState *pstate)
Definition: partbounds.c:2889
static void check_partition_bounds_for_split_range(Relation parent, char *relname, PartitionBoundSpec *spec, Oid splitPartOid, bool first, bool last, bool defaultPart, ParseState *pstate)
Definition: partbounds.c:5325
static void check_partitions_not_overlap_list(Relation parent, SinglePartitionSpec **parts, int nparts, ParseState *pstate)
Definition: partbounds.c:5259
static int get_partition_strategy(PartitionKey key)
Definition: partcache.h:59
Oid get_default_oid_from_partdesc(PartitionDesc partdesc)
Definition: partdesc.c:501
#define foreach_node(type, var, lst)
Definition: pg_list.h:496
char * relname
Definition: primnodes.h:83
PartitionBoundSpec * bound
Definition: parsenodes.h:977

References Assert(), SinglePartitionSpec::bound, check_new_partition_bound(), check_parent_values_in_new_partitions(), check_partition_bounds_for_split_list(), check_partition_bounds_for_split_range(), check_partitions_not_overlap_list(), check_two_partitions_bounds_range(), foreach_current_index, foreach_node, get_default_oid_from_partdesc(), get_partition_strategy(), i, sort-test::key, list_length(), make_one_partition_rbound(), SinglePartitionSpec::name, palloc0(), PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, pfree(), qsort_arg(), qsort_partition_rbound_cmp(), RelationGetPartitionDesc(), RelationGetPartitionKey(), and RangeVar::relname.

Referenced by transformPartitionCmdForSplit().

◆ compute_partition_hash_value()

uint64 compute_partition_hash_value ( int  partnatts,
FmgrInfo partsupfunc,
const Oid partcollation,
const Datum values,
const bool *  isnull 
)

Definition at line 4714 of file partbounds.c.

4716{
4717 int i;
4718 uint64 rowHash = 0;
4720
4721 for (i = 0; i < partnatts; i++)
4722 {
4723 /* Nulls are just ignored */
4724 if (!isnull[i])
4725 {
4726 Datum hash;
4727
4728 Assert(OidIsValid(partsupfunc[i].fn_oid));
4729
4730 /*
4731 * Compute hash for each datum value by calling respective
4732 * datatype-specific hash functions of each partition key
4733 * attribute.
4734 */
4735 hash = FunctionCall2Coll(&partsupfunc[i], partcollation[i],
4736 values[i], seed);
4737
4738 /* Form a single 64-bit hash value */
4739 rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
4740 }
4741 }
4742
4743 return rowHash;
4744}
static Datum values[MAXATTR]
Definition: bootstrap.c:153
uint64_t uint64
Definition: c.h:553
#define OidIsValid(objectId)
Definition: c.h:788
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1150
static uint64 hash_combine64(uint64 a, uint64 b)
Definition: hashfn.h:80
#define HASH_PARTITION_SEED
Definition: partition.h:20
static uint64 DatumGetUInt64(Datum X)
Definition: postgres.h:413
static Datum UInt64GetDatum(uint64 X)
Definition: postgres.h:423
static unsigned hash(unsigned *uv, int n)
Definition: rege_dfa.c:715

References Assert(), DatumGetUInt64(), FunctionCall2Coll(), hash(), hash_combine64(), HASH_PARTITION_SEED, i, OidIsValid, UInt64GetDatum(), and values.

Referenced by get_matching_hash_bounds(), and get_partition_for_tuple().

◆ get_hash_partition_greatest_modulus()

int get_hash_partition_greatest_modulus ( PartitionBoundInfo  bound)

Definition at line 3407 of file partbounds.c.

3408{
3409 Assert(bound && bound->strategy == PARTITION_STRATEGY_HASH);
3410 return bound->nindexes;
3411}

References Assert(), PartitionBoundInfoData::nindexes, PARTITION_STRATEGY_HASH, and PartitionBoundInfoData::strategy.

◆ get_qual_from_partbound()

List * get_qual_from_partbound ( Relation  parent,
PartitionBoundSpec spec 
)

Definition at line 250 of file partbounds.c.

251{
253 List *my_qual = NIL;
254
255 Assert(key != NULL);
256
257 switch (key->strategy)
258 {
261 my_qual = get_qual_for_hash(parent, spec);
262 break;
263
266 my_qual = get_qual_for_list(parent, spec);
267 break;
268
271 my_qual = get_qual_for_range(parent, spec, false);
272 break;
273 }
274
275 return my_qual;
276}
static List * get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:3975

References Assert(), get_qual_for_hash(), get_qual_for_list(), get_qual_for_range(), sort-test::key, NIL, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, RelationGetPartitionKey(), and PartitionBoundSpec::strategy.

Referenced by ATExecAttachPartition(), generate_partition_qual(), and SplitPartitionMoveRows().

◆ partition_bounds_copy()

PartitionBoundInfo partition_bounds_copy ( PartitionBoundInfo  src,
PartitionKey  key 
)

Definition at line 995 of file partbounds.c.

997{
999 int i;
1000 int ndatums;
1001 int nindexes;
1002 int partnatts;
1003
1005
1006 dest->strategy = src->strategy;
1007 ndatums = dest->ndatums = src->ndatums;
1008 nindexes = dest->nindexes = src->nindexes;
1009 partnatts = key->partnatts;
1010
1011 /* List partitioned tables have only a single partition key. */
1012 Assert(key->strategy != PARTITION_STRATEGY_LIST || partnatts == 1);
1013
1014 dest->datums = palloc_array(Datum *, ndatums);
1015
1016 if (src->kind != NULL && ndatums > 0)
1017 {
1018 PartitionRangeDatumKind *boundKinds;
1019
1020 /* only RANGE partition should have a non-NULL kind */
1021 Assert(key->strategy == PARTITION_STRATEGY_RANGE);
1022
1023 dest->kind = (PartitionRangeDatumKind **) palloc(ndatums *
1024 sizeof(PartitionRangeDatumKind *));
1025
1026 /*
1027 * In the loop below, to save from allocating a series of small arrays
1028 * for storing the PartitionRangeDatumKind, we allocate a single chunk
1029 * here and use a smaller portion of it for each datum.
1030 */
1031 boundKinds = (PartitionRangeDatumKind *) palloc(ndatums * partnatts *
1032 sizeof(PartitionRangeDatumKind));
1033
1034 for (i = 0; i < ndatums; i++)
1035 {
1036 dest->kind[i] = &boundKinds[i * partnatts];
1037 memcpy(dest->kind[i], src->kind[i],
1038 sizeof(PartitionRangeDatumKind) * partnatts);
1039 }
1040 }
1041 else
1042 dest->kind = NULL;
1043
1044 /* copy interleaved partitions for LIST partitioned tables */
1045 dest->interleaved_parts = bms_copy(src->interleaved_parts);
1046
1047 /*
1048 * For hash partitioning, datums array will have two elements - modulus
1049 * and remainder.
1050 */
1051 if (ndatums > 0)
1052 {
1053 bool hash_part = (key->strategy == PARTITION_STRATEGY_HASH);
1054 int natts = hash_part ? 2 : partnatts;
1055 Datum *boundDatums = palloc(ndatums * natts * sizeof(Datum));
1056
1057 for (i = 0; i < ndatums; i++)
1058 {
1059 int j;
1060
1061 dest->datums[i] = &boundDatums[i * natts];
1062
1063 for (j = 0; j < natts; j++)
1064 {
1065 if (dest->kind == NULL ||
1067 {
1068 bool byval;
1069 int typlen;
1070
1071 if (hash_part)
1072 {
1073 typlen = sizeof(int32); /* Always int4 */
1074 byval = true; /* int4 is pass-by-value */
1075 }
1076 else
1077 {
1078 byval = key->parttypbyval[j];
1079 typlen = key->parttyplen[j];
1080 }
1081 dest->datums[i][j] = datumCopy(src->datums[i][j],
1082 byval, typlen);
1083 }
1084 }
1085 }
1086 }
1087
1088 dest->indexes = palloc_array(int, nindexes);
1089 memcpy(dest->indexes, src->indexes, sizeof(int) * nindexes);
1090
1091 dest->null_index = src->null_index;
1092 dest->default_index = src->default_index;
1093
1094 return dest;
1095}
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:122
int32_t int32
Definition: c.h:548
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
#define palloc_object(type)
Definition: fe_memutils.h:74
#define palloc_array(type, count)
Definition: fe_memutils.h:76
int j
Definition: isn.c:78
void * palloc(Size size)
Definition: mcxt.c:1365
@ PARTITION_RANGE_DATUM_VALUE
Definition: parsenodes.h:953
struct PartitionBoundInfoData * PartitionBoundInfo
Definition: partdefs.h:16
Bitmapset * interleaved_parts
Definition: partbounds.h:87

References Assert(), bms_copy(), datumCopy(), PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, generate_unaccent_rules::dest, i, PartitionBoundInfoData::indexes, PartitionBoundInfoData::interleaved_parts, j, sort-test::key, PartitionBoundInfoData::kind, PartitionBoundInfoData::ndatums, PartitionBoundInfoData::nindexes, PartitionBoundInfoData::null_index, palloc(), palloc_array, palloc_object, PARTITION_RANGE_DATUM_VALUE, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, and PartitionBoundInfoData::strategy.

Referenced by RelationBuildPartitionDesc().

◆ partition_bounds_create()

PartitionBoundInfo partition_bounds_create ( PartitionBoundSpec **  boundspecs,
int  nparts,
PartitionKey  key,
int **  mapping 
)

Definition at line 300 of file partbounds.c.

302{
303 int i;
304
305 Assert(nparts > 0);
306
307 /*
308 * For each partitioning method, we first convert the partition bounds
309 * from their parser node representation to the internal representation,
310 * along with any additional preprocessing (such as de-duplicating range
311 * bounds). Resulting bound datums are then added to the 'datums' array
312 * in PartitionBoundInfo. For each datum added, an integer indicating the
313 * canonical partition index is added to the 'indexes' array.
314 *
315 * For each bound, we remember its partition's position (0-based) in the
316 * original list to later map it to the canonical index.
317 */
318
319 /*
320 * Initialize mapping array with invalid values, this is filled within
321 * each sub-routine below depending on the bound type.
322 */
323 *mapping = palloc_array(int, nparts);
324 for (i = 0; i < nparts; i++)
325 (*mapping)[i] = -1;
326
327 switch (key->strategy)
328 {
330 return create_hash_bounds(boundspecs, nparts, key, mapping);
331
333 return create_list_bounds(boundspecs, nparts, key, mapping);
334
336 return create_range_bounds(boundspecs, nparts, key, mapping);
337 }
338
339 Assert(false);
340 return NULL; /* keep compiler quiet */
341}
static PartitionBoundInfo create_list_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
Definition: partbounds.c:461
static PartitionBoundInfo create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
Definition: partbounds.c:348
static PartitionBoundInfo create_range_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
Definition: partbounds.c:675

References Assert(), create_hash_bounds(), create_list_bounds(), create_range_bounds(), i, sort-test::key, palloc_array, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, and PARTITION_STRATEGY_RANGE.

Referenced by RelationBuildPartitionDesc().

◆ partition_bounds_equal()

bool partition_bounds_equal ( int  partnatts,
int16 parttyplen,
bool *  parttypbyval,
PartitionBoundInfo  b1,
PartitionBoundInfo  b2 
)

Definition at line 889 of file partbounds.c.

891{
892 int i;
893
894 if (b1->strategy != b2->strategy)
895 return false;
896
897 if (b1->ndatums != b2->ndatums)
898 return false;
899
900 if (b1->nindexes != b2->nindexes)
901 return false;
902
903 if (b1->null_index != b2->null_index)
904 return false;
905
906 if (b1->default_index != b2->default_index)
907 return false;
908
909 /* For all partition strategies, the indexes[] arrays have to match */
910 for (i = 0; i < b1->nindexes; i++)
911 {
912 if (b1->indexes[i] != b2->indexes[i])
913 return false;
914 }
915
916 /* Finally, compare the datums[] arrays */
918 {
919 /*
920 * We arrange the partitions in the ascending order of their moduli
921 * and remainders. Also every modulus is factor of next larger
922 * modulus. Therefore we can safely store index of a given partition
923 * in indexes array at remainder of that partition. Also entries at
924 * (remainder + N * modulus) positions in indexes array are all same
925 * for (modulus, remainder) specification for any partition. Thus the
926 * datums arrays from the given bounds are the same, if and only if
927 * their indexes arrays are the same. So, it suffices to compare the
928 * indexes arrays.
929 *
930 * Nonetheless make sure that the bounds are indeed the same when the
931 * indexes match. Hash partition bound stores modulus and remainder
932 * at b1->datums[i][0] and b1->datums[i][1] position respectively.
933 */
934#ifdef USE_ASSERT_CHECKING
935 for (i = 0; i < b1->ndatums; i++)
936 Assert((b1->datums[i][0] == b2->datums[i][0] &&
937 b1->datums[i][1] == b2->datums[i][1]));
938#endif
939 }
940 else
941 {
942 for (i = 0; i < b1->ndatums; i++)
943 {
944 int j;
945
946 for (j = 0; j < partnatts; j++)
947 {
948 /* For range partitions, the bounds might not be finite. */
949 if (b1->kind != NULL)
950 {
951 /* The different kinds of bound all differ from each other */
952 if (b1->kind[i][j] != b2->kind[i][j])
953 return false;
954
955 /*
956 * Non-finite bounds are equal without further
957 * examination.
958 */
959 if (b1->kind[i][j] != PARTITION_RANGE_DATUM_VALUE)
960 continue;
961 }
962
963 /*
964 * Compare the actual values. Note that it would be both
965 * incorrect and unsafe to invoke the comparison operator
966 * derived from the partitioning specification here. It would
967 * be incorrect because we want the relcache entry to be
968 * updated for ANY change to the partition bounds, not just
969 * those that the partitioning operator thinks are
970 * significant. It would be unsafe because we might reach
971 * this code in the context of an aborted transaction, and an
972 * arbitrary partitioning operator might not be safe in that
973 * context. datumIsEqual() should be simple enough to be
974 * safe.
975 */
976 if (!datumIsEqual(b1->datums[i][j], b2->datums[i][j],
977 parttypbyval[j], parttyplen[j]))
978 return false;
979 }
980 }
981 }
982 return true;
983}
bool datumIsEqual(Datum value1, Datum value2, bool typByVal, int typLen)
Definition: datum.c:223

References Assert(), datumIsEqual(), PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, i, PartitionBoundInfoData::indexes, j, PartitionBoundInfoData::kind, PartitionBoundInfoData::ndatums, PartitionBoundInfoData::nindexes, PartitionBoundInfoData::null_index, PARTITION_RANGE_DATUM_VALUE, PARTITION_STRATEGY_HASH, and PartitionBoundInfoData::strategy.

Referenced by compute_partition_bounds().

◆ partition_bounds_merge()

PartitionBoundInfo partition_bounds_merge ( int  partnatts,
FmgrInfo partsupfunc,
Oid partcollation,
RelOptInfo outer_rel,
RelOptInfo inner_rel,
JoinType  jointype,
List **  outer_parts,
List **  inner_parts 
)

Definition at line 1112 of file partbounds.c.

1117{
1118 /*
1119 * Currently, this function is called only from try_partitionwise_join(),
1120 * so the join type should be INNER, LEFT, FULL, SEMI, or ANTI.
1121 */
1122 Assert(jointype == JOIN_INNER || jointype == JOIN_LEFT ||
1123 jointype == JOIN_FULL || jointype == JOIN_SEMI ||
1124 jointype == JOIN_ANTI);
1125
1126 /* The partitioning strategies should be the same. */
1127 Assert(outer_rel->boundinfo->strategy == inner_rel->boundinfo->strategy);
1128
1129 *outer_parts = *inner_parts = NIL;
1130 switch (outer_rel->boundinfo->strategy)
1131 {
1133
1134 /*
1135 * For hash partitioned tables, we currently support partitioned
1136 * join only when they have exactly the same partition bounds.
1137 *
1138 * XXX: it might be possible to relax the restriction to support
1139 * cases where hash partitioned tables have missing partitions
1140 * and/or different moduli, but it's not clear if it would be
1141 * useful to support the former case since it's unusual to have
1142 * missing partitions. On the other hand, it would be useful to
1143 * support the latter case, but in that case, there is a high
1144 * probability that a partition on one side will match multiple
1145 * partitions on the other side, which is the scenario the current
1146 * implementation of partitioned join can't handle.
1147 */
1148 return NULL;
1149
1151 return merge_list_bounds(partsupfunc,
1152 partcollation,
1153 outer_rel,
1154 inner_rel,
1155 jointype,
1156 outer_parts,
1157 inner_parts);
1158
1160 return merge_range_bounds(partnatts,
1161 partsupfunc,
1162 partcollation,
1163 outer_rel,
1164 inner_rel,
1165 jointype,
1166 outer_parts,
1167 inner_parts);
1168 }
1169
1170 return NULL;
1171}
@ JOIN_SEMI
Definition: nodes.h:317
@ JOIN_FULL
Definition: nodes.h:305
@ JOIN_INNER
Definition: nodes.h:303
@ JOIN_LEFT
Definition: nodes.h:304
@ JOIN_ANTI
Definition: nodes.h:318
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)
Definition: partbounds.c:1500
static PartitionBoundInfo merge_list_bounds(FmgrInfo *partsupfunc, Oid *partcollation, RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinType jointype, List **outer_parts, List **inner_parts)
Definition: partbounds.c:1192

References Assert(), JOIN_ANTI, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_SEMI, merge_list_bounds(), merge_range_bounds(), NIL, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, and PARTITION_STRATEGY_RANGE.

Referenced by compute_partition_bounds().

◆ partition_hash_bsearch()

int partition_hash_bsearch ( PartitionBoundInfo  boundinfo,
int  modulus,
int  remainder 
)

Definition at line 3730 of file partbounds.c.

3732{
3733 int lo,
3734 hi,
3735 mid;
3736
3737 lo = -1;
3738 hi = boundinfo->ndatums - 1;
3739 while (lo < hi)
3740 {
3741 int32 cmpval,
3742 bound_modulus,
3743 bound_remainder;
3744
3745 mid = (lo + hi + 1) / 2;
3746 bound_modulus = DatumGetInt32(boundinfo->datums[mid][0]);
3747 bound_remainder = DatumGetInt32(boundinfo->datums[mid][1]);
3748 cmpval = partition_hbound_cmp(bound_modulus, bound_remainder,
3749 modulus, remainder);
3750 if (cmpval <= 0)
3751 {
3752 lo = mid;
3753
3754 if (cmpval == 0)
3755 break;
3756 }
3757 else
3758 hi = mid - 1;
3759 }
3760
3761 return lo;
3762}
static int32 partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
Definition: partbounds.c:3579

References DatumGetInt32(), PartitionBoundInfoData::datums, PartitionBoundInfoData::ndatums, partition_hbound_cmp(), and remainder.

Referenced by check_new_partition_bound().

◆ partition_list_bsearch()

int partition_list_bsearch ( FmgrInfo partsupfunc,
Oid partcollation,
PartitionBoundInfo  boundinfo,
Datum  value,
bool *  is_equal 
)

Definition at line 3599 of file partbounds.c.

3602{
3603 int lo,
3604 hi,
3605 mid;
3606
3607 lo = -1;
3608 hi = boundinfo->ndatums - 1;
3609 while (lo < hi)
3610 {
3611 int32 cmpval;
3612
3613 mid = (lo + hi + 1) / 2;
3614 cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
3615 partcollation[0],
3616 boundinfo->datums[mid][0],
3617 value));
3618 if (cmpval <= 0)
3619 {
3620 lo = mid;
3621 *is_equal = (cmpval == 0);
3622 if (*is_equal)
3623 break;
3624 }
3625 else
3626 hi = mid - 1;
3627 }
3628
3629 return lo;
3630}
static struct @171 value

References DatumGetInt32(), PartitionBoundInfoData::datums, FunctionCall2Coll(), PartitionBoundInfoData::ndatums, and value.

Referenced by check_new_partition_bound(), check_partition_bounds_for_split_list(), get_matching_list_bounds(), and get_partition_for_tuple().

◆ partition_range_datum_bsearch()

int partition_range_datum_bsearch ( FmgrInfo partsupfunc,
Oid partcollation,
PartitionBoundInfo  boundinfo,
int  nvalues,
const Datum values,
bool *  is_equal 
)

Definition at line 3687 of file partbounds.c.

3690{
3691 int lo,
3692 hi,
3693 mid;
3694
3695 lo = -1;
3696 hi = boundinfo->ndatums - 1;
3697 while (lo < hi)
3698 {
3699 int32 cmpval;
3700
3701 mid = (lo + hi + 1) / 2;
3702 cmpval = partition_rbound_datum_cmp(partsupfunc,
3703 partcollation,
3704 boundinfo->datums[mid],
3705 boundinfo->kind[mid],
3706 values,
3707 nvalues);
3708 if (cmpval <= 0)
3709 {
3710 lo = mid;
3711 *is_equal = (cmpval == 0);
3712
3713 if (*is_equal)
3714 break;
3715 }
3716 else
3717 hi = mid - 1;
3718 }
3719
3720 return lo;
3721}
int32 partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation, const Datum *rb_datums, PartitionRangeDatumKind *rb_kind, const Datum *tuple_datums, int n_tuple_datums)
Definition: partbounds.c:3548

References PartitionBoundInfoData::datums, PartitionBoundInfoData::kind, PartitionBoundInfoData::ndatums, partition_rbound_datum_cmp(), and values.

Referenced by get_matching_range_bounds(), and get_partition_for_tuple().

◆ partition_rbound_datum_cmp()

int32 partition_rbound_datum_cmp ( FmgrInfo partsupfunc,
Oid partcollation,
const Datum rb_datums,
PartitionRangeDatumKind rb_kind,
const Datum tuple_datums,
int  n_tuple_datums 
)

Definition at line 3548 of file partbounds.c.

3551{
3552 int i;
3553 int32 cmpval = -1;
3554
3555 for (i = 0; i < n_tuple_datums; i++)
3556 {
3557 if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
3558 return -1;
3559 else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
3560 return 1;
3561
3562 cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
3563 partcollation[i],
3564 rb_datums[i],
3565 tuple_datums[i]));
3566 if (cmpval != 0)
3567 break;
3568 }
3569
3570 return cmpval;
3571}
@ PARTITION_RANGE_DATUM_MAXVALUE
Definition: parsenodes.h:954
@ PARTITION_RANGE_DATUM_MINVALUE
Definition: parsenodes.h:952

References DatumGetInt32(), FunctionCall2Coll(), i, PARTITION_RANGE_DATUM_MAXVALUE, and PARTITION_RANGE_DATUM_MINVALUE.

Referenced by get_matching_range_bounds(), get_partition_for_tuple(), and partition_range_datum_bsearch().

◆ partitions_are_ordered()

bool partitions_are_ordered ( PartitionBoundInfo  boundinfo,
Bitmapset live_parts 
)

Definition at line 2845 of file partbounds.c.

2846{
2847 Assert(boundinfo != NULL);
2848
2849 switch (boundinfo->strategy)
2850 {
2852
2853 /*
2854 * RANGE-type partitioning guarantees that the partitions can be
2855 * scanned in the order that they're defined in the PartitionDesc
2856 * to provide sequential, non-overlapping ranges of tuples.
2857 * However, if a DEFAULT partition exists and it's contained
2858 * within live_parts, then the partitions are not ordered.
2859 */
2860 if (!partition_bound_has_default(boundinfo) ||
2861 !bms_is_member(boundinfo->default_index, live_parts))
2862 return true;
2863 break;
2864
2866
2867 /*
2868 * LIST partitioned are ordered providing none of live_parts
2869 * overlap with the partitioned table's interleaved partitions.
2870 */
2871 if (!bms_overlap(live_parts, boundinfo->interleaved_parts))
2872 return true;
2873
2874 break;
2876 break;
2877 }
2878
2879 return false;
2880}
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:581

References Assert(), bms_is_member(), bms_overlap(), PartitionBoundInfoData::default_index, PartitionBoundInfoData::interleaved_parts, partition_bound_has_default, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, and PartitionBoundInfoData::strategy.

Referenced by build_partition_pathkeys(), and generate_orderedappend_paths().