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 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, struct RelOptInfo *outer_rel, struct 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, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, 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, Datum *values, bool *is_equal)
 
int partition_hash_bsearch (PartitionBoundInfo boundinfo, int modulus, int remainder)
 
void check_partitions_for_split (Relation parent, Oid splitPartOid, RangeVar *splitPartName, 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

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 5782 of file partbounds.c.

5787 {
5789  PartitionBoundSpec *bound;
5790 
5791  Assert(!spec->is_default);
5792 
5793  switch (key->strategy)
5794  {
5796  {
5797  int i;
5798  PartitionRangeBound **lower_bounds;
5799  int nparts = list_length(partOids);
5800  List *bounds = NIL;
5801 
5802  lower_bounds = (PartitionRangeBound **)
5803  palloc0(nparts * sizeof(PartitionRangeBound *));
5804 
5805  /*
5806  * Create array of lower bounds and list of
5807  * PartitionBoundSpec.
5808  */
5809  for (i = 0; i < nparts; i++)
5810  {
5811  bound = get_partition_bound_spec(list_nth_oid(partOids, i),
5812  (RangeVar *) list_nth(partNames, i));
5813 
5814  lower_bounds[i] = make_one_partition_rbound(key, i, bound->lowerdatums, true);
5815  bounds = lappend(bounds, bound);
5816  }
5817 
5818  /* Sort array of lower bounds. */
5819  qsort_arg(lower_bounds, nparts, sizeof(PartitionRangeBound *),
5820  qsort_partition_rbound_cmp, (void *) key);
5821 
5822  /* Ranges of partitions should not overlap. */
5823  for (i = 1; i < nparts; i++)
5824  {
5825  int index = lower_bounds[i]->index;
5826  int prev_index = lower_bounds[i - 1]->index;
5827 
5829  (RangeVar *) list_nth(partNames, prev_index),
5830  (PartitionBoundSpec *) list_nth(bounds, prev_index),
5831  (RangeVar *) list_nth(partNames, index),
5832  (PartitionBoundSpec *) list_nth(bounds, index),
5833  false, pstate);
5834  }
5835 
5836  /*
5837  * Lower bound of first partition is the lower bound of merged
5838  * partition.
5839  */
5840  spec->lowerdatums =
5841  ((PartitionBoundSpec *) list_nth(bounds, lower_bounds[0]->index))->lowerdatums;
5842 
5843  /*
5844  * Upper bound of last partition is the upper bound of merged
5845  * partition.
5846  */
5847  spec->upperdatums =
5848  ((PartitionBoundSpec *) list_nth(bounds, lower_bounds[nparts - 1]->index))->upperdatums;
5849 
5850  pfree(lower_bounds);
5851  list_free(bounds);
5852  break;
5853  }
5854 
5856  {
5857  ListCell *listptr,
5858  *listptr2;
5859 
5860  /* Consolidate bounds for all partitions in the list. */
5861  forboth(listptr, partOids, listptr2, partNames)
5862  {
5863  RangeVar *name = (RangeVar *) lfirst(listptr2);
5864  Oid curOid = lfirst_oid(listptr);
5865 
5866  bound = get_partition_bound_spec(curOid, name);
5867  spec->listdatums = list_concat(spec->listdatums, bound->listdatums);
5868  }
5869  break;
5870  }
5871 
5872  default:
5873  elog(ERROR, "unexpected partition strategy: %d",
5874  (int) key->strategy);
5875  }
5876 }
#define Assert(condition)
Definition: c.h:858
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
int i
Definition: isn.c:73
List * lappend(List *list, void *datum)
Definition: list.c:339
void list_free(List *list)
Definition: list.c:1546
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
void pfree(void *pointer)
Definition: mcxt.c:1520
void * palloc0(Size size)
Definition: mcxt.c:1346
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:872
@ PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:873
static PartitionBoundSpec * get_partition_bound_spec(Oid partOid, RangeVar *name)
Definition: partbounds.c:5109
static int32 qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
Definition: partbounds.c:3811
static PartitionRangeBound * make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
Definition: partbounds.c:3429
static void check_two_partitions_bounds_range(Relation parent, RangeVar *first_name, PartitionBoundSpec *first_bound, RangeVar *second_name, PartitionBoundSpec *second_bound, bool defaultPart, ParseState *pstate)
Definition: partbounds.c:5005
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:51
#define lfirst(lc)
Definition: pg_list.h:172
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
static Oid list_nth_oid(const List *list, int n)
Definition: pg_list.h:321
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define lfirst_oid(lc)
Definition: pg_list.h:174
void qsort_arg(void *base, size_t nel, size_t elsize, qsort_arg_comparator cmp, void *arg)
unsigned int Oid
Definition: postgres_ext.h:31
Definition: pg_list.h:54
Definition: type.h:95
const char * name

References Assert, check_two_partitions_bounds_range(), elog, ERROR, forboth, get_partition_bound_spec(), i, PartitionRangeBound::index, PartitionBoundSpec::is_default, sort-test::key, lappend(), lfirst, lfirst_oid, list_concat(), list_free(), list_length(), list_nth(), list_nth_oid(), PartitionBoundSpec::listdatums, PartitionBoundSpec::lowerdatums, make_one_partition_rbound(), name, 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 3252 of file partbounds.c.

3254 {
3255  List *new_part_constraints;
3256  List *def_part_constraints;
3257  List *all_parts;
3258  ListCell *lc;
3259 
3260  new_part_constraints = (new_spec->strategy == PARTITION_STRATEGY_LIST)
3261  ? get_qual_for_list(parent, new_spec)
3262  : get_qual_for_range(parent, new_spec, false);
3263  def_part_constraints =
3264  get_proposed_default_constraint(new_part_constraints);
3265 
3266  /*
3267  * Map the Vars in the constraint expression from parent's attnos to
3268  * default_rel's.
3269  */
3270  def_part_constraints =
3271  map_partition_varattnos(def_part_constraints, 1, default_rel,
3272  parent);
3273 
3274  /*
3275  * If the existing constraints on the default partition imply that it will
3276  * not contain any row that would belong to the new partition, we can
3277  * avoid scanning the default partition.
3278  */
3279  if (PartConstraintImpliedByRelConstraint(default_rel, def_part_constraints))
3280  {
3281  ereport(DEBUG1,
3282  (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
3283  RelationGetRelationName(default_rel))));
3284  return;
3285  }
3286 
3287  /*
3288  * Scan the default partition and its subpartitions, and check for rows
3289  * that do not satisfy the revised partition constraints.
3290  */
3291  if (default_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
3292  all_parts = find_all_inheritors(RelationGetRelid(default_rel),
3293  AccessExclusiveLock, NULL);
3294  else
3295  all_parts = list_make1_oid(RelationGetRelid(default_rel));
3296 
3297  foreach(lc, all_parts)
3298  {
3299  Oid part_relid = lfirst_oid(lc);
3300  Relation part_rel;
3301  Expr *partition_constraint;
3302  EState *estate;
3303  ExprState *partqualstate = NULL;
3304  Snapshot snapshot;
3305  ExprContext *econtext;
3306  TableScanDesc scan;
3307  MemoryContext oldCxt;
3308  TupleTableSlot *tupslot;
3309 
3310  /* Lock already taken above. */
3311  if (part_relid != RelationGetRelid(default_rel))
3312  {
3313  part_rel = table_open(part_relid, NoLock);
3314 
3315  /*
3316  * Map the Vars in the constraint expression from default_rel's
3317  * the sub-partition's.
3318  */
3319  partition_constraint = make_ands_explicit(def_part_constraints);
3320  partition_constraint = (Expr *)
3321  map_partition_varattnos((List *) partition_constraint, 1,
3322  part_rel, default_rel);
3323 
3324  /*
3325  * If the partition constraints on default partition child imply
3326  * that it will not contain any row that would belong to the new
3327  * partition, we can avoid scanning the child table.
3328  */
3330  def_part_constraints))
3331  {
3332  ereport(DEBUG1,
3333  (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
3334  RelationGetRelationName(part_rel))));
3335 
3336  table_close(part_rel, NoLock);
3337  continue;
3338  }
3339  }
3340  else
3341  {
3342  part_rel = default_rel;
3343  partition_constraint = make_ands_explicit(def_part_constraints);
3344  }
3345 
3346  /*
3347  * Only RELKIND_RELATION relations (i.e. leaf partitions) need to be
3348  * scanned.
3349  */
3350  if (part_rel->rd_rel->relkind != RELKIND_RELATION)
3351  {
3352  if (part_rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
3353  ereport(WARNING,
3354  (errcode(ERRCODE_CHECK_VIOLATION),
3355  errmsg("skipped scanning foreign table \"%s\" which is a partition of default partition \"%s\"",
3356  RelationGetRelationName(part_rel),
3357  RelationGetRelationName(default_rel))));
3358 
3359  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
3360  table_close(part_rel, NoLock);
3361 
3362  continue;
3363  }
3364 
3365  estate = CreateExecutorState();
3366 
3367  /* Build expression execution states for partition check quals */
3368  partqualstate = ExecPrepareExpr(partition_constraint, estate);
3369 
3370  econtext = GetPerTupleExprContext(estate);
3371  snapshot = RegisterSnapshot(GetLatestSnapshot());
3372  tupslot = table_slot_create(part_rel, &estate->es_tupleTable);
3373  scan = table_beginscan(part_rel, snapshot, 0, NULL);
3374 
3375  /*
3376  * Switch to per-tuple memory context and reset it for each tuple
3377  * produced, so we don't leak memory.
3378  */
3380 
3381  while (table_scan_getnextslot(scan, ForwardScanDirection, tupslot))
3382  {
3383  econtext->ecxt_scantuple = tupslot;
3384 
3385  if (!ExecCheck(partqualstate, econtext))
3386  ereport(ERROR,
3387  (errcode(ERRCODE_CHECK_VIOLATION),
3388  errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
3389  RelationGetRelationName(default_rel)),
3390  errtable(default_rel)));
3391 
3392  ResetExprContext(econtext);
3394  }
3395 
3396  MemoryContextSwitchTo(oldCxt);
3397  table_endscan(scan);
3398  UnregisterSnapshot(snapshot);
3400  FreeExecutorState(estate);
3401 
3402  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
3403  table_close(part_rel, NoLock); /* keep the lock until commit */
3404  }
3405 }
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1157
int errcode(int sqlerrcode)
Definition: elog.c:857
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define WARNING
Definition: elog.h:36
#define DEBUG1
Definition: elog.h:30
#define ereport(elevel,...)
Definition: elog.h:149
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:739
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition: execExpr.c:846
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1341
EState * CreateExecutorState(void)
Definition: execUtils.c:88
void FreeExecutorState(EState *estate)
Definition: execUtils.c:189
#define GetPerTupleExprContext(estate)
Definition: executor.h:550
#define ResetExprContext(econtext)
Definition: executor.h:544
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:555
#define NoLock
Definition: lockdefs.h:34
#define AccessExclusiveLock
Definition: lockdefs.h:43
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:726
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
static List * get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:4067
static List * get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default)
Definition: partbounds.c:4276
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
MemoryContextSwitchTo(old_ctx)
#define RelationGetRelid(relation)
Definition: rel.h:505
#define RelationGetRelationName(relation)
Definition: rel.h:539
int errtable(Relation rel)
Definition: relcache.c:5945
@ ForwardScanDirection
Definition: sdir.h:28
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:291
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:836
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:794
List * es_tupleTable
Definition: execnodes.h:669
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:255
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:91
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, struct ScanKeyData *key)
Definition: tableam.h:918
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:1029
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:1065
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:19022

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 2896 of file partbounds.c.

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

Definition at line 5563 of file partbounds.c.

5568 {
5569  PartitionKey key;
5570  char strategy;
5571  Oid defaultPartOid;
5572  bool isSplitPartDefault;
5573  bool existsDefaultPart;
5574  ListCell *listptr;
5575  int default_index = -1;
5576  int i,
5577  j;
5578  SinglePartitionSpec **new_parts;
5579  SinglePartitionSpec *spsPrev = NULL;
5580  int nparts = 0;
5581 
5582  key = RelationGetPartitionKey(parent);
5583  strategy = get_partition_strategy(key);
5584 
5585  switch (strategy)
5586  {
5589  {
5590  /*
5591  * Make array new_parts with new partitions except DEFAULT
5592  * partition.
5593  */
5594  new_parts = (SinglePartitionSpec **)
5595  palloc0(list_length(partlist) * sizeof(SinglePartitionSpec *));
5596  i = 0;
5597  foreach(listptr, partlist)
5598  {
5599  SinglePartitionSpec *sps =
5600  (SinglePartitionSpec *) lfirst(listptr);
5601 
5602  if (sps->bound->is_default)
5603  {
5604  if (default_index >= 0)
5605  ereport(ERROR,
5606  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5607  errmsg("DEFAULT partition should be one")),
5608  parser_errposition(pstate, sps->name->location));
5609  default_index = i;
5610  }
5611  else
5612  {
5613  new_parts[nparts++] = sps;
5614  }
5615  i++;
5616  }
5617  }
5618  break;
5619 
5621  ereport(ERROR,
5622  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5623  errmsg("partition of hash-partitioned table cannot be split")));
5624  break;
5625 
5626  default:
5627  elog(ERROR, "unexpected partition strategy: %d",
5628  (int) key->strategy);
5629  break;
5630  }
5631 
5632  if (strategy == PARTITION_STRATEGY_RANGE)
5633  {
5634  PartitionRangeBound **lower_bounds;
5635  SinglePartitionSpec **tmp_new_parts;
5636 
5637  /*
5638  * For simplify check for ranges of new partitions need to sort all
5639  * partitions in ascending order of them bounds (we compare upper
5640  * bound only).
5641  */
5642  lower_bounds = (PartitionRangeBound **)
5643  palloc0(nparts * sizeof(PartitionRangeBound *));
5644 
5645  /* Create array of lower bounds. */
5646  for (i = 0; i < nparts; i++)
5647  {
5648  lower_bounds[i] = make_one_partition_rbound(key, i,
5649  new_parts[i]->bound->lowerdatums, true);
5650  }
5651 
5652  /* Sort array of lower bounds. */
5653  qsort_arg(lower_bounds, nparts, sizeof(PartitionRangeBound *),
5654  qsort_partition_rbound_cmp, (void *) key);
5655 
5656  /* Reorder array of partitions. */
5657  tmp_new_parts = new_parts;
5658  new_parts = (SinglePartitionSpec **)
5659  palloc0(nparts * sizeof(SinglePartitionSpec *));
5660  for (i = 0; i < nparts; i++)
5661  new_parts[i] = tmp_new_parts[lower_bounds[i]->index];
5662 
5663  pfree(tmp_new_parts);
5664  pfree(lower_bounds);
5665  }
5666 
5667  defaultPartOid =
5669 
5670  /* isSplitPartDefault flag: is split partition a DEFAULT partition? */
5671  isSplitPartDefault = (defaultPartOid == splitPartOid);
5672 
5673  if (isSplitPartDefault && default_index < 0)
5674  {
5675  ereport(ERROR,
5676  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5677  errmsg("one partition in the list should be DEFAULT because split partition is DEFAULT")),
5678  parser_errposition(pstate, ((SinglePartitionSpec *) linitial(partlist))->name->location));
5679  }
5680  else if (!isSplitPartDefault && (default_index >= 0) && OidIsValid(defaultPartOid))
5681  {
5682  SinglePartitionSpec *spsDef =
5683  (SinglePartitionSpec *) list_nth(partlist, default_index);
5684 
5685  ereport(ERROR,
5686  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5687  errmsg("new partition cannot be DEFAULT because DEFAULT partition already exists")),
5688  parser_errposition(pstate, spsDef->name->location));
5689  }
5690 
5691  /* Indicator that partitioned table has (or will have) DEFAULT partition */
5692  existsDefaultPart = OidIsValid(defaultPartOid) || (default_index >= 0);
5693 
5694  for (i = 0; i < nparts; i++)
5695  {
5696  SinglePartitionSpec *sps = new_parts[i];
5697 
5698  if (isSplitPartDefault)
5699  {
5700  /*
5701  * In case split partition is DEFAULT partition we can use any
5702  * free ranges - as when creating a new partition.
5703  */
5704  check_new_partition_bound(sps->name->relname, parent, sps->bound,
5705  pstate);
5706  }
5707  else
5708  {
5709  /*
5710  * Checks that bound of current partition is inside bound of split
5711  * partition. For range partitioning: checks that upper bound of
5712  * previous partition is equal to lower bound of current
5713  * partition. For list partitioning: checks that split partition
5714  * contains all values of current partition.
5715  */
5716  if (strategy == PARTITION_STRATEGY_RANGE)
5717  {
5718  bool first = (i == 0);
5719  bool last = (i == (nparts - 1));
5720 
5722  splitPartOid, splitPartName,
5723  first, last,
5724  existsDefaultPart, pstate);
5725  }
5726  else
5728  sps->bound, splitPartOid, pstate);
5729  }
5730 
5731  /* Ranges of new partitions should not overlap. */
5732  if (strategy == PARTITION_STRATEGY_RANGE && spsPrev)
5733  check_two_partitions_bounds_range(parent, spsPrev->name, spsPrev->bound,
5734  sps->name, sps->bound, existsDefaultPart, pstate);
5735 
5736  spsPrev = sps;
5737 
5738  /* Check: new partitions should have different names. */
5739  for (j = i + 1; j < nparts; j++)
5740  {
5741  SinglePartitionSpec *sps2 = new_parts[j];
5742 
5743  if (equal(sps->name, sps2->name))
5744  ereport(ERROR,
5745  (errcode(ERRCODE_DUPLICATE_TABLE),
5746  errmsg("name \"%s\" is already used", sps2->name->relname)),
5747  parser_errposition(pstate, sps2->name->location));
5748  }
5749  }
5750 
5751  if (strategy == PARTITION_STRATEGY_LIST)
5752  {
5753  /* Values of new partitions should not overlap. */
5754  check_partitions_not_overlap_list(parent, new_parts, nparts,
5755  pstate);
5756 
5757  /*
5758  * Need to check that all values of split partition contains in new
5759  * partitions. Skip this check if DEFAULT partition exists.
5760  */
5761  if (!existsDefaultPart)
5762  check_parent_values_in_new_partitions(parent, splitPartOid,
5763  new_parts, nparts, pstate);
5764  }
5765 
5766  pfree(new_parts);
5767 }
#define OidIsValid(objectId)
Definition: c.h:775
int j
Definition: isn.c:74
static void check_partition_bounds_for_split_list(Relation parent, char *relname, PartitionBoundSpec *spec, Oid splitPartOid, ParseState *pstate)
Definition: partbounds.c:5316
static void check_parent_values_in_new_partitions(Relation parent, Oid partOid, SinglePartitionSpec **parts, int nparts, ParseState *pstate)
Definition: partbounds.c:5460
static void check_partition_bounds_for_split_range(Relation parent, char *relname, PartitionBoundSpec *spec, Oid splitPartOid, RangeVar *splitPartName, bool first, bool last, bool defaultPart, ParseState *pstate)
Definition: partbounds.c:5163
void check_new_partition_bound(char *relname, Relation parent, PartitionBoundSpec *spec, ParseState *pstate)
Definition: partbounds.c:2896
static void check_partitions_not_overlap_list(Relation parent, SinglePartitionSpec **parts, int nparts, ParseState *pstate)
Definition: partbounds.c:5057
static int get_partition_strategy(PartitionKey key)
Definition: partcache.h:59
Oid get_default_oid_from_partdesc(PartitionDesc partdesc)
Definition: partdesc.c:459
char * relname
Definition: primnodes.h:82
ParseLoc location
Definition: primnodes.h:94
PartitionBoundSpec * bound
Definition: parsenodes.h:948

References 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(), elog, equal(), ereport, errcode(), errmsg(), ERROR, get_default_oid_from_partdesc(), get_partition_strategy(), i, PartitionBoundSpec::is_default, j, sort-test::key, lfirst, linitial, list_length(), list_nth(), RangeVar::location, make_one_partition_rbound(), name, SinglePartitionSpec::name, OidIsValid, palloc0(), parser_errposition(), PARTITION_STRATEGY_HASH, 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 4723 of file partbounds.c.

4725 {
4726  int i;
4727  uint64 rowHash = 0;
4729 
4730  for (i = 0; i < partnatts; i++)
4731  {
4732  /* Nulls are just ignored */
4733  if (!isnull[i])
4734  {
4735  Datum hash;
4736 
4737  Assert(OidIsValid(partsupfunc[i].fn_oid));
4738 
4739  /*
4740  * Compute hash for each datum value by calling respective
4741  * datatype-specific hash functions of each partition key
4742  * attribute.
4743  */
4744  hash = FunctionCall2Coll(&partsupfunc[i], partcollation[i],
4745  values[i], seed);
4746 
4747  /* Form a single 64-bit hash value */
4748  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
4749  }
4750  }
4751 
4752  return rowHash;
4753 }
static Datum values[MAXATTR]
Definition: bootstrap.c:152
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1149
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:419
static Datum UInt64GetDatum(uint64 X)
Definition: postgres.h:436
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 3415 of file partbounds.c.

3416 {
3417  Assert(bound && bound->strategy == PARTITION_STRATEGY_HASH);
3418  return bound->nindexes;
3419 }

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 249 of file partbounds.c.

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

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 moveSplitTableRows().

◆ partition_bounds_copy()

PartitionBoundInfo partition_bounds_copy ( PartitionBoundInfo  src,
PartitionKey  key 
)

Definition at line 1002 of file partbounds.c.

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

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

References Assert, create_hash_bounds(), create_list_bounds(), create_range_bounds(), i, sort-test::key, palloc(), 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 896 of file partbounds.c.

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

Definition at line 1118 of file partbounds.c.

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

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 3739 of file partbounds.c.

3741 {
3742  int lo,
3743  hi,
3744  mid;
3745 
3746  lo = -1;
3747  hi = boundinfo->ndatums - 1;
3748  while (lo < hi)
3749  {
3750  int32 cmpval,
3751  bound_modulus,
3752  bound_remainder;
3753 
3754  mid = (lo + hi + 1) / 2;
3755  bound_modulus = DatumGetInt32(boundinfo->datums[mid][0]);
3756  bound_remainder = DatumGetInt32(boundinfo->datums[mid][1]);
3757  cmpval = partition_hbound_cmp(bound_modulus, bound_remainder,
3758  modulus, remainder);
3759  if (cmpval <= 0)
3760  {
3761  lo = mid;
3762 
3763  if (cmpval == 0)
3764  break;
3765  }
3766  else
3767  hi = mid - 1;
3768  }
3769 
3770  return lo;
3771 }
static int32 partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
Definition: partbounds.c:3588

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

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 3608 of file partbounds.c.

3611 {
3612  int lo,
3613  hi,
3614  mid;
3615 
3616  lo = -1;
3617  hi = boundinfo->ndatums - 1;
3618  while (lo < hi)
3619  {
3620  int32 cmpval;
3621 
3622  mid = (lo + hi + 1) / 2;
3623  cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
3624  partcollation[0],
3625  boundinfo->datums[mid][0],
3626  value));
3627  if (cmpval <= 0)
3628  {
3629  lo = mid;
3630  *is_equal = (cmpval == 0);
3631  if (*is_equal)
3632  break;
3633  }
3634  else
3635  hi = mid - 1;
3636  }
3637 
3638  return lo;
3639 }
static struct @155 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,
Datum values,
bool is_equal 
)

Definition at line 3696 of file partbounds.c.

3699 {
3700  int lo,
3701  hi,
3702  mid;
3703 
3704  lo = -1;
3705  hi = boundinfo->ndatums - 1;
3706  while (lo < hi)
3707  {
3708  int32 cmpval;
3709 
3710  mid = (lo + hi + 1) / 2;
3711  cmpval = partition_rbound_datum_cmp(partsupfunc,
3712  partcollation,
3713  boundinfo->datums[mid],
3714  boundinfo->kind[mid],
3715  values,
3716  nvalues);
3717  if (cmpval <= 0)
3718  {
3719  lo = mid;
3720  *is_equal = (cmpval == 0);
3721 
3722  if (*is_equal)
3723  break;
3724  }
3725  else
3726  hi = mid - 1;
3727  }
3728 
3729  return lo;
3730 }
int32 partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *tuple_datums, int n_tuple_datums)
Definition: partbounds.c:3557

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,
Datum rb_datums,
PartitionRangeDatumKind rb_kind,
Datum tuple_datums,
int  n_tuple_datums 
)

Definition at line 3557 of file partbounds.c.

3560 {
3561  int i;
3562  int32 cmpval = -1;
3563 
3564  for (i = 0; i < n_tuple_datums; i++)
3565  {
3566  if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
3567  return -1;
3568  else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
3569  return 1;
3570 
3571  cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
3572  partcollation[i],
3573  rb_datums[i],
3574  tuple_datums[i]));
3575  if (cmpval != 0)
3576  break;
3577  }
3578 
3579  return cmpval;
3580 }
@ PARTITION_RANGE_DATUM_MAXVALUE
Definition: parsenodes.h:926
@ PARTITION_RANGE_DATUM_MINVALUE
Definition: parsenodes.h:924

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 2852 of file partbounds.c.

2853 {
2854  Assert(boundinfo != NULL);
2855 
2856  switch (boundinfo->strategy)
2857  {
2859 
2860  /*
2861  * RANGE-type partitioning guarantees that the partitions can be
2862  * scanned in the order that they're defined in the PartitionDesc
2863  * to provide sequential, non-overlapping ranges of tuples.
2864  * However, if a DEFAULT partition exists and it's contained
2865  * within live_parts, then the partitions are not ordered.
2866  */
2867  if (!partition_bound_has_default(boundinfo) ||
2868  !bms_is_member(boundinfo->default_index, live_parts))
2869  return true;
2870  break;
2871 
2873 
2874  /*
2875  * LIST partitioned are ordered providing none of live_parts
2876  * overlap with the partitioned table's interleaved partitions.
2877  */
2878  if (!bms_overlap(live_parts, boundinfo->interleaved_parts))
2879  return true;
2880 
2881  break;
2883  break;
2884  }
2885 
2886  return false;
2887 }
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:582

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().