PostgreSQL Source Code  git master
partbounds.c File Reference
#include "postgres.h"
#include "access/relation.h"
#include "access/table.h"
#include "access/tableam.h"
#include "catalog/partition.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_type.h"
#include "commands/tablecmds.h"
#include "common/hashfn.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/pathnodes.h"
#include "parser/parse_coerce.h"
#include "partitioning/partbounds.h"
#include "partitioning/partdesc.h"
#include "partitioning/partprune.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/partcache.h"
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
Include dependency graph for partbounds.c:

Go to the source code of this file.

Data Structures

struct  PartitionHashBound
 
struct  PartitionListValue
 
struct  PartitionRangeBound
 
struct  PartitionMap
 

Macros

#define compare_range_bounds(partnatts, partsupfunc, partcollations, bound1, bound2)
 

Typedefs

typedef struct PartitionHashBound PartitionHashBound
 
typedef struct PartitionListValue PartitionListValue
 
typedef struct PartitionRangeBound PartitionRangeBound
 
typedef struct PartitionMap PartitionMap
 

Functions

static int32 qsort_partition_hbound_cmp (const void *a, const void *b)
 
static int32 qsort_partition_list_value_cmp (const void *a, const void *b, void *arg)
 
static int32 qsort_partition_rbound_cmp (const void *a, const void *b, void *arg)
 
static PartitionBoundInfo create_hash_bounds (PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
 
static PartitionBoundInfo create_list_bounds (PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
 
static PartitionBoundInfo create_range_bounds (PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
 
static PartitionBoundInfo merge_list_bounds (FmgrInfo *partsupfunc, Oid *collations, RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinType jointype, List **outer_parts, List **inner_parts)
 
static PartitionBoundInfo merge_range_bounds (int partnatts, FmgrInfo *partsupfuncs, Oid *partcollations, RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinType jointype, List **outer_parts, List **inner_parts)
 
static void init_partition_map (RelOptInfo *rel, PartitionMap *map)
 
static void free_partition_map (PartitionMap *map)
 
static bool is_dummy_partition (RelOptInfo *rel, int part_index)
 
static int merge_matching_partitions (PartitionMap *outer_map, PartitionMap *inner_map, int outer_part, int inner_part, int *next_index)
 
static int process_outer_partition (PartitionMap *outer_map, PartitionMap *inner_map, bool outer_has_default, bool inner_has_default, int outer_index, int inner_default, JoinType jointype, int *next_index, int *default_index)
 
static int process_inner_partition (PartitionMap *outer_map, PartitionMap *inner_map, bool outer_has_default, bool inner_has_default, int inner_index, int outer_default, JoinType jointype, int *next_index, int *default_index)
 
static void merge_null_partitions (PartitionMap *outer_map, PartitionMap *inner_map, bool outer_has_null, bool inner_has_null, int outer_null, int inner_null, JoinType jointype, int *next_index, int *null_index)
 
static void merge_default_partitions (PartitionMap *outer_map, PartitionMap *inner_map, bool outer_has_default, bool inner_has_default, int outer_default, int inner_default, JoinType jointype, int *next_index, int *default_index)
 
static int merge_partition_with_dummy (PartitionMap *map, int index, int *next_index)
 
static void fix_merged_indexes (PartitionMap *outer_map, PartitionMap *inner_map, int nmerged, List *merged_indexes)
 
static void generate_matching_part_pairs (RelOptInfo *outer_rel, RelOptInfo *inner_rel, PartitionMap *outer_map, PartitionMap *inner_map, int nmerged, List **outer_parts, List **inner_parts)
 
static PartitionBoundInfo build_merged_partition_bounds (char strategy, List *merged_datums, List *merged_kinds, List *merged_indexes, int null_index, int default_index)
 
static int get_range_partition (RelOptInfo *rel, PartitionBoundInfo bi, int *lb_pos, PartitionRangeBound *lb, PartitionRangeBound *ub)
 
static int get_range_partition_internal (PartitionBoundInfo bi, int *lb_pos, PartitionRangeBound *lb, PartitionRangeBound *ub)
 
static bool compare_range_partitions (int partnatts, FmgrInfo *partsupfuncs, Oid *partcollations, PartitionRangeBound *outer_lb, PartitionRangeBound *outer_ub, PartitionRangeBound *inner_lb, PartitionRangeBound *inner_ub, int *lb_cmpval, int *ub_cmpval)
 
static void get_merged_range_bounds (int partnatts, FmgrInfo *partsupfuncs, Oid *partcollations, JoinType jointype, PartitionRangeBound *outer_lb, PartitionRangeBound *outer_ub, PartitionRangeBound *inner_lb, PartitionRangeBound *inner_ub, int lb_cmpval, int ub_cmpval, PartitionRangeBound *merged_lb, PartitionRangeBound *merged_ub)
 
static void add_merged_range_bounds (int partnatts, FmgrInfo *partsupfuncs, Oid *partcollations, PartitionRangeBound *merged_lb, PartitionRangeBound *merged_ub, int merged_index, List **merged_datums, List **merged_kinds, List **merged_indexes)
 
static PartitionRangeBoundmake_one_partition_rbound (PartitionKey key, int index, List *datums, bool lower)
 
static int32 partition_hbound_cmp (int modulus1, int remainder1, int modulus2, int remainder2)
 
static int32 partition_rbound_cmp (int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
 
static int partition_range_bsearch (int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, PartitionRangeBound *probe, int32 *cmpval)
 
static int get_partition_bound_num_indexes (PartitionBoundInfo b)
 
static Exprmake_partition_op_expr (PartitionKey key, int keynum, uint16 strategy, Expr *arg1, Expr *arg2)
 
static Oid get_partition_operator (PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel)
 
static Listget_qual_for_hash (Relation parent, PartitionBoundSpec *spec)
 
static Listget_qual_for_list (Relation parent, PartitionBoundSpec *spec)
 
static Listget_qual_for_range (Relation parent, PartitionBoundSpec *spec, bool for_default)
 
static void get_range_key_properties (PartitionKey key, int keynum, PartitionRangeDatum *ldatum, PartitionRangeDatum *udatum, ListCell **partexprs_item, Expr **keyCol, Const **lower_val, Const **upper_val)
 
static Listget_range_nulltest (PartitionKey key)
 
Listget_qual_from_partbound (Relation rel, 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, int nparts)
 
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)
 
int get_hash_partition_greatest_modulus (PartitionBoundInfo bound)
 
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)
 
uint64 compute_partition_hash_value (int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *values, bool *isnull)
 
Datum satisfies_hash_partition (PG_FUNCTION_ARGS)
 

Macro Definition Documentation

◆ compare_range_bounds

#define compare_range_bounds (   partnatts,
  partsupfunc,
  partcollations,
  bound1,
  bound2 
)
Value:
(partition_rbound_cmp(partnatts, partsupfunc, partcollations, \
(bound1)->datums, (bound1)->kind, (bound1)->lower, \
bound2))
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:44
static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partbounds.c:3368

Definition at line 88 of file partbounds.c.

Referenced by compare_range_partitions(), get_merged_range_bounds(), and merge_range_bounds().

Typedef Documentation

◆ PartitionHashBound

◆ PartitionListValue

◆ PartitionMap

typedef struct PartitionMap PartitionMap

◆ PartitionRangeBound

Function Documentation

◆ add_merged_range_bounds()

static void add_merged_range_bounds ( int  partnatts,
FmgrInfo partsupfuncs,
Oid partcollations,
PartitionRangeBound merged_lb,
PartitionRangeBound merged_ub,
int  merged_index,
List **  merged_datums,
List **  merged_kinds,
List **  merged_indexes 
)
static

Definition at line 2673 of file partbounds.c.

References Assert, PartitionRangeBound::datums, PartitionRangeBound::index, PartitionRangeBound::kind, lappend(), lappend_int(), llast, llast_int, PartitionRangeBound::lower, and partition_rbound_cmp().

Referenced by merge_range_bounds().

2681 {
2682  int cmpval;
2683 
2684  if (!*merged_datums)
2685  {
2686  /* First merged partition */
2687  Assert(!*merged_kinds);
2688  Assert(!*merged_indexes);
2689  cmpval = 1;
2690  }
2691  else
2692  {
2693  PartitionRangeBound prev_ub;
2694 
2695  Assert(*merged_datums);
2696  Assert(*merged_kinds);
2697  Assert(*merged_indexes);
2698 
2699  /* Get the last upper bound. */
2700  prev_ub.index = llast_int(*merged_indexes);
2701  prev_ub.datums = (Datum *) llast(*merged_datums);
2702  prev_ub.kind = (PartitionRangeDatumKind *) llast(*merged_kinds);
2703  prev_ub.lower = false;
2704 
2705  /*
2706  * We pass to partition_rbound_cmp() lower1 as false to prevent it
2707  * from considering the last upper bound to be smaller than the lower
2708  * bound of the merged partition when the values of the two range
2709  * bounds compare equal.
2710  */
2711  cmpval = partition_rbound_cmp(partnatts, partsupfuncs, partcollations,
2712  merged_lb->datums, merged_lb->kind,
2713  false, &prev_ub);
2714  Assert(cmpval >= 0);
2715  }
2716 
2717  /*
2718  * If the lower bound is higher than the last upper bound, add the lower
2719  * bound with the index as -1 indicating that that is a lower bound; else,
2720  * the last upper bound will be reused as the lower bound of the merged
2721  * partition, so skip this.
2722  */
2723  if (cmpval > 0)
2724  {
2725  *merged_datums = lappend(*merged_datums, merged_lb->datums);
2726  *merged_kinds = lappend(*merged_kinds, merged_lb->kind);
2727  *merged_indexes = lappend_int(*merged_indexes, -1);
2728  }
2729 
2730  /* Add the upper bound and index of the merged partition. */
2731  *merged_datums = lappend(*merged_datums, merged_ub->datums);
2732  *merged_kinds = lappend(*merged_kinds, merged_ub->kind);
2733  *merged_indexes = lappend_int(*merged_indexes, merged_index);
2734 }
PartitionRangeDatumKind * kind
Definition: partbounds.c:68
PartitionRangeDatumKind
Definition: parsenodes.h:837
#define llast(l)
Definition: pg_list.h:194
List * lappend_int(List *list, int datum)
Definition: list.c:339
List * lappend(List *list, void *datum)
Definition: list.c:321
uintptr_t Datum
Definition: postgres.h:367
static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partbounds.c:3368
#define Assert(condition)
Definition: c.h:746
#define llast_int(l)
Definition: pg_list.h:195

◆ build_merged_partition_bounds()

static PartitionBoundInfo build_merged_partition_bounds ( char  strategy,
List merged_datums,
List merged_kinds,
List merged_indexes,
int  null_index,
int  default_index 
)
static

Definition at line 2420 of file partbounds.c.

References Assert, PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, PartitionBoundInfoData::indexes, PartitionBoundInfoData::kind, lappend_int(), lfirst, lfirst_int, list_length(), PartitionBoundInfoData::ndatums, NIL, PartitionBoundInfoData::null_index, palloc(), PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, and PartitionBoundInfoData::strategy.

Referenced by merge_list_bounds(), and merge_range_bounds().

2423 {
2424  PartitionBoundInfo merged_bounds;
2425  int ndatums = list_length(merged_datums);
2426  int pos;
2427  ListCell *lc;
2428 
2429  merged_bounds = (PartitionBoundInfo) palloc(sizeof(PartitionBoundInfoData));
2430  merged_bounds->strategy = strategy;
2431  merged_bounds->ndatums = ndatums;
2432 
2433  merged_bounds->datums = (Datum **) palloc(sizeof(Datum *) * ndatums);
2434  pos = 0;
2435  foreach(lc, merged_datums)
2436  merged_bounds->datums[pos++] = (Datum *) lfirst(lc);
2437 
2438  if (strategy == PARTITION_STRATEGY_RANGE)
2439  {
2440  Assert(list_length(merged_kinds) == ndatums);
2441  merged_bounds->kind = (PartitionRangeDatumKind **)
2442  palloc(sizeof(PartitionRangeDatumKind *) * ndatums);
2443  pos = 0;
2444  foreach(lc, merged_kinds)
2445  merged_bounds->kind[pos++] = (PartitionRangeDatumKind *) lfirst(lc);
2446 
2447  /* There are ndatums+1 indexes in the case of range partitioning. */
2448  merged_indexes = lappend_int(merged_indexes, -1);
2449  ndatums++;
2450  }
2451  else
2452  {
2453  Assert(strategy == PARTITION_STRATEGY_LIST);
2454  Assert(merged_kinds == NIL);
2455  merged_bounds->kind = NULL;
2456  }
2457 
2458  Assert(list_length(merged_indexes) == ndatums);
2459  merged_bounds->indexes = (int *) palloc(sizeof(int) * ndatums);
2460  pos = 0;
2461  foreach(lc, merged_indexes)
2462  merged_bounds->indexes[pos++] = lfirst_int(lc);
2463 
2464  merged_bounds->null_index = null_index;
2465  merged_bounds->default_index = default_index;
2466 
2467  return merged_bounds;
2468 }
#define NIL
Definition: pg_list.h:65
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
PartitionRangeDatumKind
Definition: parsenodes.h:837
#define lfirst_int(lc)
Definition: pg_list.h:170
List * lappend_int(List *list, int datum)
Definition: list.c:339
uintptr_t Datum
Definition: postgres.h:367
#define Assert(condition)
Definition: c.h:746
#define lfirst(lc)
Definition: pg_list.h:169
static int list_length(const List *l)
Definition: pg_list.h:149
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
void * palloc(Size size)
Definition: mcxt.c:950
struct PartitionBoundInfoData * PartitionBoundInfo
Definition: partdefs.h:16

◆ check_default_partition_contents()

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

Definition at line 3128 of file partbounds.c.

References AccessExclusiveLock, CHECK_FOR_INTERRUPTS, CreateExecutorState(), DEBUG1, ExprContext::ecxt_scantuple, ereport, errcode(), errmsg(), 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().

3130 {
3131  List *new_part_constraints;
3132  List *def_part_constraints;
3133  List *all_parts;
3134  ListCell *lc;
3135 
3136  new_part_constraints = (new_spec->strategy == PARTITION_STRATEGY_LIST)
3137  ? get_qual_for_list(parent, new_spec)
3138  : get_qual_for_range(parent, new_spec, false);
3139  def_part_constraints =
3140  get_proposed_default_constraint(new_part_constraints);
3141 
3142  /*
3143  * Map the Vars in the constraint expression from parent's attnos to
3144  * default_rel's.
3145  */
3146  def_part_constraints =
3147  map_partition_varattnos(def_part_constraints, 1, default_rel,
3148  parent);
3149 
3150  /*
3151  * If the existing constraints on the default partition imply that it will
3152  * not contain any row that would belong to the new partition, we can
3153  * avoid scanning the default partition.
3154  */
3155  if (PartConstraintImpliedByRelConstraint(default_rel, def_part_constraints))
3156  {
3157  ereport(DEBUG1,
3158  (errmsg("updated partition constraint for default partition \"%s\" is implied by existing constraints",
3159  RelationGetRelationName(default_rel))));
3160  return;
3161  }
3162 
3163  /*
3164  * Scan the default partition and its subpartitions, and check for rows
3165  * that do not satisfy the revised partition constraints.
3166  */
3167  if (default_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
3168  all_parts = find_all_inheritors(RelationGetRelid(default_rel),
3169  AccessExclusiveLock, NULL);
3170  else
3171  all_parts = list_make1_oid(RelationGetRelid(default_rel));
3172 
3173  foreach(lc, all_parts)
3174  {
3175  Oid part_relid = lfirst_oid(lc);
3176  Relation part_rel;
3177  Expr *partition_constraint;
3178  EState *estate;
3179  ExprState *partqualstate = NULL;
3180  Snapshot snapshot;
3181  ExprContext *econtext;
3182  TableScanDesc scan;
3183  MemoryContext oldCxt;
3184  TupleTableSlot *tupslot;
3185 
3186  /* Lock already taken above. */
3187  if (part_relid != RelationGetRelid(default_rel))
3188  {
3189  part_rel = table_open(part_relid, NoLock);
3190 
3191  /*
3192  * Map the Vars in the constraint expression from default_rel's
3193  * the sub-partition's.
3194  */
3195  partition_constraint = make_ands_explicit(def_part_constraints);
3196  partition_constraint = (Expr *)
3197  map_partition_varattnos((List *) partition_constraint, 1,
3198  part_rel, default_rel);
3199 
3200  /*
3201  * If the partition constraints on default partition child imply
3202  * that it will not contain any row that would belong to the new
3203  * partition, we can avoid scanning the child table.
3204  */
3206  def_part_constraints))
3207  {
3208  ereport(DEBUG1,
3209  (errmsg("updated partition constraint for default partition \"%s\" is implied by existing constraints",
3210  RelationGetRelationName(part_rel))));
3211 
3212  table_close(part_rel, NoLock);
3213  continue;
3214  }
3215  }
3216  else
3217  {
3218  part_rel = default_rel;
3219  partition_constraint = make_ands_explicit(def_part_constraints);
3220  }
3221 
3222  /*
3223  * Only RELKIND_RELATION relations (i.e. leaf partitions) need to be
3224  * scanned.
3225  */
3226  if (part_rel->rd_rel->relkind != RELKIND_RELATION)
3227  {
3228  if (part_rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
3229  ereport(WARNING,
3230  (errcode(ERRCODE_CHECK_VIOLATION),
3231  errmsg("skipped scanning foreign table \"%s\" which is a partition of default partition \"%s\"",
3232  RelationGetRelationName(part_rel),
3233  RelationGetRelationName(default_rel))));
3234 
3235  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
3236  table_close(part_rel, NoLock);
3237 
3238  continue;
3239  }
3240 
3241  estate = CreateExecutorState();
3242 
3243  /* Build expression execution states for partition check quals */
3244  partqualstate = ExecPrepareExpr(partition_constraint, estate);
3245 
3246  econtext = GetPerTupleExprContext(estate);
3247  snapshot = RegisterSnapshot(GetLatestSnapshot());
3248  tupslot = table_slot_create(part_rel, &estate->es_tupleTable);
3249  scan = table_beginscan(part_rel, snapshot, 0, NULL);
3250 
3251  /*
3252  * Switch to per-tuple memory context and reset it for each tuple
3253  * produced, so we don't leak memory.
3254  */
3256 
3257  while (table_scan_getnextslot(scan, ForwardScanDirection, tupslot))
3258  {
3259  econtext->ecxt_scantuple = tupslot;
3260 
3261  if (!ExecCheck(partqualstate, econtext))
3262  ereport(ERROR,
3263  (errcode(ERRCODE_CHECK_VIOLATION),
3264  errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
3265  RelationGetRelationName(default_rel)),
3266  errtable(default_rel)));
3267 
3268  ResetExprContext(econtext);
3270  }
3271 
3272  MemoryContextSwitchTo(oldCxt);
3273  table_endscan(scan);
3274  UnregisterSnapshot(snapshot);
3276  FreeExecutorState(estate);
3277 
3278  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
3279  table_close(part_rel, NoLock); /* keep the lock until commit */
3280  }
3281 }
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91
#define DEBUG1
Definition: elog.h:25
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:810
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int errcode(int sqlerrcode)
Definition: elog.c:610
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:904
Form_pg_class rd_rel
Definition: rel.h:109
unsigned int Oid
Definition: postgres_ext.h:31
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:492
void FreeExecutorState(EState *estate)
Definition: execUtils.c:185
#define GetPerTupleExprContext(estate)
Definition: executor.h:506
#define ERROR
Definition: elog.h:43
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, struct ScanKeyData *key)
Definition: tableam.h:755
#define NoLock
Definition: lockdefs.h:34
List * get_proposed_default_constraint(List *new_part_constraints)
Definition: partition.c:345
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1224
#define RelationGetRelationName(relation)
Definition: rel.h:490
EState * CreateExecutorState(void)
Definition: execUtils.c:89
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:852
#define WARNING
Definition: elog.h:40
List * es_tupleTable
Definition: execnodes.h:561
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:707
#define list_make1_oid(x1)
Definition: pg_list.h:228
#define ereport(elevel,...)
Definition: elog.h:144
static List * get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default)
Definition: partbounds.c:4192
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:225
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:325
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:511
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:863
#define AccessExclusiveLock
Definition: lockdefs.h:45
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:165
int errmsg(const char *fmt,...)
Definition: elog.c:821
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition: execExpr.c:599
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
Definition: pg_list.h:50
int errtable(Relation rel)
Definition: relcache.c:5497
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition: partition.c:198
#define RelationGetRelid(relation)
Definition: rel.h:456
static List * get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:3983
#define ResetExprContext(econtext)
Definition: executor.h:500
#define lfirst_oid(lc)
Definition: pg_list.h:171
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:16107

◆ check_new_partition_bound()

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

Definition at line 2807 of file partbounds.c.

References Assert, PartitionDescData::boundinfo, castNode, Const::constisnull, Const::constvalue, DatumGetInt32, PartitionBoundInfoData::datums, PartitionRangeBound::datums, PartitionBoundInfoData::default_index, elog, equal(), ereport, errcode(), errdetail(), errmsg(), ERROR, get_hash_partition_greatest_modulus(), get_range_partbound_string(), get_rel_name(), PartitionBoundInfoData::indexes, PartitionBoundSpec::is_default, sort-test::key, PartitionBoundInfoData::kind, PartitionRangeBound::kind, lfirst, linitial, list_nth(), PartitionBoundSpec::listdatums, Const::location, PartitionBoundSpec::location, PartitionRangeDatum::location, lower(), PartitionBoundSpec::lowerdatums, make_one_partition_rbound(), PartitionBoundSpec::modulus, PartitionBoundInfoData::ndatums, PartitionDescData::nparts, PartitionBoundInfoData::null_index, PartitionDescData::oids, parser_errposition(), PartitionKeyData::partcollation, 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, PartitionKeyData::partnatts, PartitionKeyData::partsupfunc, RelationGetPartitionDesc(), RelationGetPartitionKey(), PartitionHashBound::remainder, PartitionBoundSpec::remainder, PartitionKeyData::strategy, PartitionBoundInfoData::strategy, PartitionBoundSpec::strategy, upper(), PartitionBoundSpec::upperdatums, and val.

Referenced by ATExecAttachPartition(), and DefineRelation().

2809 {
2811  PartitionDesc partdesc = RelationGetPartitionDesc(parent);
2812  PartitionBoundInfo boundinfo = partdesc->boundinfo;
2813  int with = -1;
2814  bool overlap = false;
2815  int overlap_location = -1;
2816 
2817  if (spec->is_default)
2818  {
2819  /*
2820  * The default partition bound never conflicts with any other
2821  * partition's; if that's what we're attaching, the only possible
2822  * problem is that one already exists, so check for that and we're
2823  * done.
2824  */
2825  if (boundinfo == NULL || !partition_bound_has_default(boundinfo))
2826  return;
2827 
2828  /* Default partition already exists, error out. */
2829  ereport(ERROR,
2830  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2831  errmsg("partition \"%s\" conflicts with existing default partition \"%s\"",
2832  relname, get_rel_name(partdesc->oids[boundinfo->default_index])),
2833  parser_errposition(pstate, spec->location)));
2834  }
2835 
2836  switch (key->strategy)
2837  {
2839  {
2841  Assert(spec->remainder >= 0 && spec->remainder < spec->modulus);
2842 
2843  if (partdesc->nparts > 0)
2844  {
2845  Datum **datums = boundinfo->datums;
2846  int ndatums = boundinfo->ndatums;
2847  int greatest_modulus;
2848  int remainder;
2849  int offset;
2850  bool valid_modulus = true;
2851  int prev_modulus, /* Previous largest modulus */
2852  next_modulus; /* Next largest modulus */
2853 
2854  /*
2855  * Check rule that every modulus must be a factor of the
2856  * next larger modulus. For example, if you have a bunch
2857  * of partitions that all have modulus 5, you can add a
2858  * new partition with modulus 10 or a new partition with
2859  * modulus 15, but you cannot add both a partition with
2860  * modulus 10 and a partition with modulus 15, because 10
2861  * is not a factor of 15.
2862  *
2863  * Get the greatest (modulus, remainder) pair contained in
2864  * boundinfo->datums that is less than or equal to the
2865  * (spec->modulus, spec->remainder) pair.
2866  */
2867  offset = partition_hash_bsearch(boundinfo,
2868  spec->modulus,
2869  spec->remainder);
2870  if (offset < 0)
2871  {
2872  next_modulus = DatumGetInt32(datums[0][0]);
2873  valid_modulus = (next_modulus % spec->modulus) == 0;
2874  }
2875  else
2876  {
2877  prev_modulus = DatumGetInt32(datums[offset][0]);
2878  valid_modulus = (spec->modulus % prev_modulus) == 0;
2879 
2880  if (valid_modulus && (offset + 1) < ndatums)
2881  {
2882  next_modulus = DatumGetInt32(datums[offset + 1][0]);
2883  valid_modulus = (next_modulus % spec->modulus) == 0;
2884  }
2885  }
2886 
2887  if (!valid_modulus)
2888  ereport(ERROR,
2889  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2890  errmsg("every hash partition modulus must be a factor of the next larger modulus")));
2891 
2892  greatest_modulus = get_hash_partition_greatest_modulus(boundinfo);
2893  remainder = spec->remainder;
2894 
2895  /*
2896  * Normally, the lowest remainder that could conflict with
2897  * the new partition is equal to the remainder specified
2898  * for the new partition, but when the new partition has a
2899  * modulus higher than any used so far, we need to adjust.
2900  */
2901  if (remainder >= greatest_modulus)
2902  remainder = remainder % greatest_modulus;
2903 
2904  /* Check every potentially-conflicting remainder. */
2905  do
2906  {
2907  if (boundinfo->indexes[remainder] != -1)
2908  {
2909  overlap = true;
2910  overlap_location = spec->location;
2911  with = boundinfo->indexes[remainder];
2912  break;
2913  }
2914  remainder += spec->modulus;
2915  } while (remainder < greatest_modulus);
2916  }
2917 
2918  break;
2919  }
2920 
2922  {
2924 
2925  if (partdesc->nparts > 0)
2926  {
2927  ListCell *cell;
2928 
2929  Assert(boundinfo &&
2930  boundinfo->strategy == PARTITION_STRATEGY_LIST &&
2931  (boundinfo->ndatums > 0 ||
2932  partition_bound_accepts_nulls(boundinfo) ||
2933  partition_bound_has_default(boundinfo)));
2934 
2935  foreach(cell, spec->listdatums)
2936  {
2937  Const *val = castNode(Const, lfirst(cell));
2938 
2939  overlap_location = val->location;
2940  if (!val->constisnull)
2941  {
2942  int offset;
2943  bool equal;
2944 
2945  offset = partition_list_bsearch(&key->partsupfunc[0],
2946  key->partcollation,
2947  boundinfo,
2948  val->constvalue,
2949  &equal);
2950  if (offset >= 0 && equal)
2951  {
2952  overlap = true;
2953  with = boundinfo->indexes[offset];
2954  break;
2955  }
2956  }
2957  else if (partition_bound_accepts_nulls(boundinfo))
2958  {
2959  overlap = true;
2960  with = boundinfo->null_index;
2961  break;
2962  }
2963  }
2964  }
2965 
2966  break;
2967  }
2968 
2970  {
2972  *upper;
2973  int cmpval;
2974 
2976  lower = make_one_partition_rbound(key, -1, spec->lowerdatums, true);
2977  upper = make_one_partition_rbound(key, -1, spec->upperdatums, false);
2978 
2979  /*
2980  * First check if the resulting range would be empty with
2981  * specified lower and upper bounds
2982  */
2983  cmpval = partition_rbound_cmp(key->partnatts,
2984  key->partsupfunc,
2985  key->partcollation,
2986  lower->datums, lower->kind,
2987  true, upper);
2988  if (cmpval >= 0)
2989  {
2990  /* Fetch the problematic key from the lower datums list. */
2991  PartitionRangeDatum *datum = list_nth(spec->lowerdatums,
2992  cmpval - 1);
2993 
2994  ereport(ERROR,
2995  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2996  errmsg("empty range bound specified for partition \"%s\"",
2997  relname),
2998  errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
3001  parser_errposition(pstate, datum->location)));
3002  }
3003 
3004  if (partdesc->nparts > 0)
3005  {
3006  int offset;
3007 
3008  Assert(boundinfo &&
3009  boundinfo->strategy == PARTITION_STRATEGY_RANGE &&
3010  (boundinfo->ndatums > 0 ||
3011  partition_bound_has_default(boundinfo)));
3012 
3013  /*
3014  * Test whether the new lower bound (which is treated
3015  * inclusively as part of the new partition) lies inside
3016  * an existing partition, or in a gap.
3017  *
3018  * If it's inside an existing partition, the bound at
3019  * offset + 1 will be the upper bound of that partition,
3020  * and its index will be >= 0.
3021  *
3022  * If it's in a gap, the bound at offset + 1 will be the
3023  * lower bound of the next partition, and its index will
3024  * be -1. This is also true if there is no next partition,
3025  * since the index array is initialised with an extra -1
3026  * at the end.
3027  */
3028  offset = partition_range_bsearch(key->partnatts,
3029  key->partsupfunc,
3030  key->partcollation,
3031  boundinfo, lower,
3032  &cmpval);
3033 
3034  if (boundinfo->indexes[offset + 1] < 0)
3035  {
3036  /*
3037  * Check that the new partition will fit in the gap.
3038  * For it to fit, the new upper bound must be less
3039  * than or equal to the lower bound of the next
3040  * partition, if there is one.
3041  */
3042  if (offset + 1 < boundinfo->ndatums)
3043  {
3044  Datum *datums;
3046  bool is_lower;
3047 
3048  datums = boundinfo->datums[offset + 1];
3049  kind = boundinfo->kind[offset + 1];
3050  is_lower = (boundinfo->indexes[offset + 1] == -1);
3051 
3052  cmpval = partition_rbound_cmp(key->partnatts,
3053  key->partsupfunc,
3054  key->partcollation,
3055  datums, kind,
3056  is_lower, upper);
3057  if (cmpval < 0)
3058  {
3059  /*
3060  * Fetch the problematic key from the upper
3061  * datums list.
3062  */
3063  PartitionRangeDatum *datum =
3064  list_nth(spec->upperdatums, -cmpval - 1);
3065 
3066  /*
3067  * The new partition overlaps with the
3068  * existing partition between offset + 1 and
3069  * offset + 2.
3070  */
3071  overlap = true;
3072  overlap_location = datum->location;
3073  with = boundinfo->indexes[offset + 2];
3074  }
3075  }
3076  }
3077  else
3078  {
3079  /*
3080  * The new partition overlaps with the existing
3081  * partition between offset and offset + 1.
3082  */
3083  PartitionRangeDatum *datum;
3084 
3085  /*
3086  * Fetch the problematic key from the lower datums
3087  * list. Given the way partition_range_bsearch()
3088  * works, the new lower bound is certainly >= the
3089  * bound at offset. If the bound matches exactly, we
3090  * flag the 1st key.
3091  */
3092  Assert(cmpval >= 0);
3093  datum = cmpval == 0 ? linitial(spec->lowerdatums) :
3094  list_nth(spec->lowerdatums, cmpval - 1);
3095  overlap = true;
3096  overlap_location = datum->location;
3097  with = boundinfo->indexes[offset + 1];
3098  }
3099  }
3100 
3101  break;
3102  }
3103 
3104  default:
3105  elog(ERROR, "unexpected partition strategy: %d",
3106  (int) key->strategy);
3107  }
3108 
3109  if (overlap)
3110  {
3111  Assert(with >= 0);
3112  ereport(ERROR,
3113  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3114  errmsg("partition \"%s\" would overlap partition \"%s\"",
3115  relname, get_rel_name(partdesc->oids[with])),
3116  parser_errposition(pstate, overlap_location)));
3117  }
3118 }
Datum constvalue
Definition: primnodes.h:214
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
PartitionRangeDatumKind * kind
Definition: partbounds.c:68
#define DatumGetInt32(X)
Definition: postgres.h:472
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3032
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:44
FmgrInfo * partsupfunc
Definition: partcache.h:35
#define castNode(_type_, nodeptr)
Definition: nodes.h:597
PartitionRangeDatumKind
Definition: parsenodes.h:837
int errcode(int sqlerrcode)
Definition: elog.c:610
Datum upper(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:75
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
NameData relname
Definition: pg_class.h:38
PartitionBoundInfo boundinfo
Definition: partdesc.h:29
static PartitionRangeBound * make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
Definition: partbounds.c:3308
#define linitial(l)
Definition: pg_list.h:174
#define ERROR
Definition: elog.h:43
static void * list_nth(const List *list, int n)
Definition: pg_list.h:266
int get_hash_partition_greatest_modulus(PartitionBoundInfo bound)
Definition: partbounds.c:3291
int errdetail(const char *fmt,...)
Definition: elog.c:954
PartitionDesc RelationGetPartitionDesc(Relation rel)
Definition: partdesc.c:65
#define partition_bound_has_default(bi)
Definition: partbounds.h:75
int partition_hash_bsearch(PartitionBoundInfo boundinfo, int modulus, int remainder)
Definition: partbounds.c:3617
char * get_range_partbound_string(List *bound_datums)
Definition: ruleutils.c:11355
Oid * partcollation
Definition: partcache.h:38
int location
Definition: primnodes.h:221
uintptr_t Datum
Definition: postgres.h:367
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:74
int partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
Definition: partbounds.c:3486
#define ereport(elevel,...)
Definition: elog.h:144
static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partbounds.c:3368
#define Assert(condition)
Definition: c.h:746
#define lfirst(lc)
Definition: pg_list.h:169
static int partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, PartitionRangeBound *probe, int32 *cmpval)
Definition: partbounds.c:3532
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:110
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
int errmsg(const char *fmt,...)
Definition: elog.c:821
#define elog(elevel,...)
Definition: elog.h:214
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1840
long val
Definition: informix.c:664
bool constisnull
Definition: primnodes.h:215

◆ compare_range_partitions()

static bool compare_range_partitions ( int  partnatts,
FmgrInfo partsupfuncs,
Oid partcollations,
PartitionRangeBound outer_lb,
PartitionRangeBound outer_ub,
PartitionRangeBound inner_lb,
PartitionRangeBound inner_ub,
int *  lb_cmpval,
int *  ub_cmpval 
)
static

Definition at line 2560 of file partbounds.c.

References compare_range_bounds.

Referenced by merge_range_bounds().

2567 {
2568  /*
2569  * Check if the outer partition's upper bound is lower than the inner
2570  * partition's lower bound; if so the partitions aren't overlapping.
2571  */
2572  if (compare_range_bounds(partnatts, partsupfuncs, partcollations,
2573  outer_ub, inner_lb) < 0)
2574  {
2575  *lb_cmpval = -1;
2576  *ub_cmpval = -1;
2577  return false;
2578  }
2579 
2580  /*
2581  * Check if the outer partition's lower bound is higher than the inner
2582  * partition's upper bound; if so the partitions aren't overlapping.
2583  */
2584  if (compare_range_bounds(partnatts, partsupfuncs, partcollations,
2585  outer_lb, inner_ub) > 0)
2586  {
2587  *lb_cmpval = 1;
2588  *ub_cmpval = 1;
2589  return false;
2590  }
2591 
2592  /* All other cases indicate overlapping partitions. */
2593  *lb_cmpval = compare_range_bounds(partnatts, partsupfuncs, partcollations,
2594  outer_lb, inner_lb);
2595  *ub_cmpval = compare_range_bounds(partnatts, partsupfuncs, partcollations,
2596  outer_ub, inner_ub);
2597  return true;
2598 }
#define compare_range_bounds(partnatts, partsupfunc, partcollations, bound1, bound2)
Definition: partbounds.c:88

◆ compute_partition_hash_value()

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

Definition at line 4644 of file partbounds.c.

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

Referenced by get_matching_hash_bounds(), and get_partition_for_tuple().

4646 {
4647  int i;
4648  uint64 rowHash = 0;
4650 
4651  for (i = 0; i < partnatts; i++)
4652  {
4653  /* Nulls are just ignored */
4654  if (!isnull[i])
4655  {
4656  Datum hash;
4657 
4658  Assert(OidIsValid(partsupfunc[i].fn_oid));
4659 
4660  /*
4661  * Compute hash for each datum value by calling respective
4662  * datatype-specific hash functions of each partition key
4663  * attribute.
4664  */
4665  hash = FunctionCall2Coll(&partsupfunc[i], partcollation[i],
4666  values[i], seed);
4667 
4668  /* Form a single 64-bit hash value */
4669  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
4670  }
4671  }
4672 
4673  return rowHash;
4674 }
#define UInt64GetDatum(X)
Definition: postgres.h:648
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1152
static uint64 hash_combine64(uint64 a, uint64 b)
Definition: hashfn.h:80
#define OidIsValid(objectId)
Definition: c.h:652
uintptr_t Datum
Definition: postgres.h:367
#define DatumGetUInt64(X)
Definition: postgres.h:634
#define Assert(condition)
Definition: c.h:746
#define HASH_PARTITION_SEED
Definition: partition.h:20
static Datum values[MAXATTR]
Definition: bootstrap.c:165
int i
static unsigned hash(unsigned *uv, int n)
Definition: rege_dfa.c:541

◆ create_hash_bounds()

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

Definition at line 358 of file partbounds.c.

References Assert, PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, elog, ERROR, i, PartitionHashBound::index, PartitionBoundInfoData::indexes, Int32GetDatum, PartitionHashBound::modulus, PartitionBoundSpec::modulus, PartitionBoundInfoData::ndatums, PartitionBoundInfoData::null_index, palloc(), palloc0(), PARTITION_STRATEGY_HASH, pfree(), qsort, qsort_partition_hbound_cmp(), PartitionHashBound::remainder, PartitionBoundSpec::remainder, PartitionKeyData::strategy, PartitionBoundInfoData::strategy, and PartitionBoundSpec::strategy.

Referenced by partition_bounds_create().

360 {
361  PartitionBoundInfo boundinfo;
362  PartitionHashBound **hbounds = NULL;
363  int i;
364  int ndatums = 0;
365  int greatest_modulus;
366 
367  boundinfo = (PartitionBoundInfoData *)
369  boundinfo->strategy = key->strategy;
370  /* No special hash partitions. */
371  boundinfo->null_index = -1;
372  boundinfo->default_index = -1;
373 
374  ndatums = nparts;
375  hbounds = (PartitionHashBound **)
376  palloc(nparts * sizeof(PartitionHashBound *));
377 
378  /* Convert from node to the internal representation */
379  for (i = 0; i < nparts; i++)
380  {
381  PartitionBoundSpec *spec = boundspecs[i];
382 
383  if (spec->strategy != PARTITION_STRATEGY_HASH)
384  elog(ERROR, "invalid strategy in partition bound spec");
385 
386  hbounds[i] = (PartitionHashBound *) palloc(sizeof(PartitionHashBound));
387  hbounds[i]->modulus = spec->modulus;
388  hbounds[i]->remainder = spec->remainder;
389  hbounds[i]->index = i;
390  }
391 
392  /* Sort all the bounds in ascending order */
393  qsort(hbounds, nparts, sizeof(PartitionHashBound *),
395 
396  /* After sorting, moduli are now stored in ascending order. */
397  greatest_modulus = hbounds[ndatums - 1]->modulus;
398 
399  boundinfo->ndatums = ndatums;
400  boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *));
401  boundinfo->indexes = (int *) palloc(greatest_modulus * sizeof(int));
402  for (i = 0; i < greatest_modulus; i++)
403  boundinfo->indexes[i] = -1;
404 
405  /*
406  * For hash partitioning, there are as many datums (modulus and remainder
407  * pairs) as there are partitions. Indexes are simply values ranging from
408  * 0 to (nparts - 1).
409  */
410  for (i = 0; i < nparts; i++)
411  {
412  int modulus = hbounds[i]->modulus;
413  int remainder = hbounds[i]->remainder;
414 
415  boundinfo->datums[i] = (Datum *) palloc(2 * sizeof(Datum));
416  boundinfo->datums[i][0] = Int32GetDatum(modulus);
417  boundinfo->datums[i][1] = Int32GetDatum(remainder);
418 
419  while (remainder < greatest_modulus)
420  {
421  /* overlap? */
422  Assert(boundinfo->indexes[remainder] == -1);
423  boundinfo->indexes[remainder] = i;
424  remainder += modulus;
425  }
426 
427  (*mapping)[hbounds[i]->index] = i;
428  pfree(hbounds[i]);
429  }
430  pfree(hbounds);
431 
432  return boundinfo;
433 }
static int32 qsort_partition_hbound_cmp(const void *a, const void *b)
Definition: partbounds.c:3657
void pfree(void *pointer)
Definition: mcxt.c:1057
#define ERROR
Definition: elog.h:43
void * palloc0(Size size)
Definition: mcxt.c:981
uintptr_t Datum
Definition: postgres.h:367
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
#define Assert(condition)
Definition: c.h:746
#define Int32GetDatum(X)
Definition: postgres.h:479
void * palloc(Size size)
Definition: mcxt.c:950
#define elog(elevel,...)
Definition: elog.h:214
int i
#define qsort(a, b, c, d)
Definition: port.h:497

◆ create_list_bounds()

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

Definition at line 440 of file partbounds.c.

References Assert, castNode, Const::constisnull, Const::constvalue, datumCopy(), PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, elog, ERROR, i, PartitionListValue::index, PartitionBoundInfoData::indexes, PartitionBoundSpec::is_default, lappend(), lfirst, list_length(), PartitionBoundSpec::listdatums, PartitionBoundInfoData::ndatums, NIL, PartitionBoundInfoData::null_index, palloc(), palloc0(), PARTITION_STRATEGY_LIST, qsort_arg(), qsort_partition_list_value_cmp(), PartitionKeyData::strategy, PartitionBoundInfoData::strategy, PartitionBoundSpec::strategy, val, PartitionListValue::value, and value.

Referenced by partition_bounds_create().

442 {
443  PartitionBoundInfo boundinfo;
444  PartitionListValue **all_values = NULL;
445  ListCell *cell;
446  int i = 0;
447  int ndatums = 0;
448  int next_index = 0;
449  int default_index = -1;
450  int null_index = -1;
451  List *non_null_values = NIL;
452 
453  boundinfo = (PartitionBoundInfoData *)
455  boundinfo->strategy = key->strategy;
456  /* Will be set correctly below. */
457  boundinfo->null_index = -1;
458  boundinfo->default_index = -1;
459 
460  /* Create a unified list of non-null values across all partitions. */
461  for (i = 0; i < nparts; i++)
462  {
463  PartitionBoundSpec *spec = boundspecs[i];
464  ListCell *c;
465 
466  if (spec->strategy != PARTITION_STRATEGY_LIST)
467  elog(ERROR, "invalid strategy in partition bound spec");
468 
469  /*
470  * Note the index of the partition bound spec for the default
471  * partition. There's no datum to add to the list on non-null datums
472  * for this partition.
473  */
474  if (spec->is_default)
475  {
476  default_index = i;
477  continue;
478  }
479 
480  foreach(c, spec->listdatums)
481  {
482  Const *val = castNode(Const, lfirst(c));
483  PartitionListValue *list_value = NULL;
484 
485  if (!val->constisnull)
486  {
487  list_value = (PartitionListValue *)
488  palloc0(sizeof(PartitionListValue));
489  list_value->index = i;
490  list_value->value = val->constvalue;
491  }
492  else
493  {
494  /*
495  * Never put a null into the values array; save the index of
496  * the partition that stores nulls, instead.
497  */
498  if (null_index != -1)
499  elog(ERROR, "found null more than once");
500  null_index = i;
501  }
502 
503  if (list_value)
504  non_null_values = lappend(non_null_values, list_value);
505  }
506  }
507 
508  ndatums = list_length(non_null_values);
509 
510  /*
511  * Collect all list values in one array. Alongside the value, we also save
512  * the index of partition the value comes from.
513  */
514  all_values = (PartitionListValue **)
515  palloc(ndatums * sizeof(PartitionListValue *));
516  i = 0;
517  foreach(cell, non_null_values)
518  {
519  PartitionListValue *src = lfirst(cell);
520 
521  all_values[i] = (PartitionListValue *)
522  palloc(sizeof(PartitionListValue));
523  all_values[i]->value = src->value;
524  all_values[i]->index = src->index;
525  i++;
526  }
527 
528  qsort_arg(all_values, ndatums, sizeof(PartitionListValue *),
529  qsort_partition_list_value_cmp, (void *) key);
530 
531  boundinfo->ndatums = ndatums;
532  boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *));
533  boundinfo->indexes = (int *) palloc(ndatums * sizeof(int));
534 
535  /*
536  * Copy values. Canonical indexes are values ranging from 0 to (nparts -
537  * 1) assigned to each partition such that all datums of a given partition
538  * receive the same value. The value for a given partition is the index of
539  * that partition's smallest datum in the all_values[] array.
540  */
541  for (i = 0; i < ndatums; i++)
542  {
543  int orig_index = all_values[i]->index;
544 
545  boundinfo->datums[i] = (Datum *) palloc(sizeof(Datum));
546  boundinfo->datums[i][0] = datumCopy(all_values[i]->value,
547  key->parttypbyval[0],
548  key->parttyplen[0]);
549 
550  /* If the old index has no mapping, assign one */
551  if ((*mapping)[orig_index] == -1)
552  (*mapping)[orig_index] = next_index++;
553 
554  boundinfo->indexes[i] = (*mapping)[orig_index];
555  }
556 
557  /*
558  * Set the canonical value for null_index, if any.
559  *
560  * It is possible that the null-accepting partition has not been assigned
561  * an index yet, which could happen if such partition accepts only null
562  * and hence not handled in the above loop which only looked at non-null
563  * values.
564  */
565  if (null_index != -1)
566  {
567  Assert(null_index >= 0);
568  if ((*mapping)[null_index] == -1)
569  (*mapping)[null_index] = next_index++;
570  boundinfo->null_index = (*mapping)[null_index];
571  }
572 
573  /* Set the canonical value for default_index, if any. */
574  if (default_index != -1)
575  {
576  /*
577  * The default partition accepts any value not specified in the lists
578  * of other partitions, hence it should not get mapped index while
579  * assigning those for non-null datums.
580  */
581  Assert(default_index >= 0);
582  Assert((*mapping)[default_index] == -1);
583  (*mapping)[default_index] = next_index++;
584  boundinfo->default_index = (*mapping)[default_index];
585  }
586 
587  /* All partitions must now have been assigned canonical indexes. */
588  Assert(next_index == nparts);
589  return boundinfo;
590 }
Datum constvalue
Definition: primnodes.h:214
#define NIL
Definition: pg_list.h:65
#define castNode(_type_, nodeptr)
Definition: nodes.h:597
#define ERROR
Definition: elog.h:43
char * c
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:131
List * lappend(List *list, void *datum)
Definition: list.c:321
void qsort_arg(void *base, size_t nel, size_t elsize, qsort_arg_comparator cmp, void *arg)
Definition: qsort_arg.c:113
void * palloc0(Size size)
Definition: mcxt.c:981
uintptr_t Datum
Definition: postgres.h:367
static struct @143 value
bool * parttypbyval
Definition: partcache.h:44
#define Assert(condition)
Definition: c.h:746
#define lfirst(lc)
Definition: pg_list.h:169
int16 * parttyplen
Definition: partcache.h:43
static int list_length(const List *l)
Definition: pg_list.h:149
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
void * palloc(Size size)
Definition: mcxt.c:950
#define elog(elevel,...)
Definition: elog.h:214
int i
static int32 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
Definition: partbounds.c:3672
Definition: pg_list.h:50
long val
Definition: informix.c:664
bool constisnull
Definition: primnodes.h:215

◆ create_range_bounds()

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

Definition at line 597 of file partbounds.c.

References Assert, cur, datumCopy(), DatumGetInt32, PartitionBoundInfoData::datums, PartitionRangeBound::datums, PartitionBoundInfoData::default_index, elog, ERROR, FunctionCall2Coll(), i, PartitionRangeBound::index, PartitionBoundInfoData::indexes, PartitionBoundSpec::is_default, PartitionBoundInfoData::kind, PartitionRangeBound::kind, lower(), PartitionBoundSpec::lowerdatums, make_one_partition_rbound(), PartitionBoundInfoData::ndatums, PartitionBoundInfoData::null_index, palloc(), palloc0(), PARTITION_RANGE_DATUM_VALUE, PARTITION_STRATEGY_RANGE, qsort_arg(), qsort_partition_rbound_cmp(), PartitionKeyData::strategy, PartitionBoundInfoData::strategy, PartitionBoundSpec::strategy, upper(), and PartitionBoundSpec::upperdatums.

Referenced by partition_bounds_create().

599 {
600  PartitionBoundInfo boundinfo;
601  PartitionRangeBound **rbounds = NULL;
602  PartitionRangeBound **all_bounds,
603  *prev;
604  int i,
605  k;
606  int ndatums = 0;
607  int default_index = -1;
608  int next_index = 0;
609 
610  boundinfo = (PartitionBoundInfoData *)
612  boundinfo->strategy = key->strategy;
613  /* There is no special null-accepting range partition. */
614  boundinfo->null_index = -1;
615  /* Will be set correctly below. */
616  boundinfo->default_index = -1;
617 
618  all_bounds = (PartitionRangeBound **)
619  palloc0(2 * nparts * sizeof(PartitionRangeBound *));
620 
621  /* Create a unified list of range bounds across all the partitions. */
622  ndatums = 0;
623  for (i = 0; i < nparts; i++)
624  {
625  PartitionBoundSpec *spec = boundspecs[i];
627  *upper;
628 
629  if (spec->strategy != PARTITION_STRATEGY_RANGE)
630  elog(ERROR, "invalid strategy in partition bound spec");
631 
632  /*
633  * Note the index of the partition bound spec for the default
634  * partition. There's no datum to add to the all_bounds array for
635  * this partition.
636  */
637  if (spec->is_default)
638  {
639  default_index = i;
640  continue;
641  }
642 
643  lower = make_one_partition_rbound(key, i, spec->lowerdatums, true);
644  upper = make_one_partition_rbound(key, i, spec->upperdatums, false);
645  all_bounds[ndatums++] = lower;
646  all_bounds[ndatums++] = upper;
647  }
648 
649  Assert(ndatums == nparts * 2 ||
650  (default_index != -1 && ndatums == (nparts - 1) * 2));
651 
652  /* Sort all the bounds in ascending order */
653  qsort_arg(all_bounds, ndatums,
654  sizeof(PartitionRangeBound *),
656  (void *) key);
657 
658  /* Save distinct bounds from all_bounds into rbounds. */
659  rbounds = (PartitionRangeBound **)
660  palloc(ndatums * sizeof(PartitionRangeBound *));
661  k = 0;
662  prev = NULL;
663  for (i = 0; i < ndatums; i++)
664  {
665  PartitionRangeBound *cur = all_bounds[i];
666  bool is_distinct = false;
667  int j;
668 
669  /* Is the current bound distinct from the previous one? */
670  for (j = 0; j < key->partnatts; j++)
671  {
672  Datum cmpval;
673 
674  if (prev == NULL || cur->kind[j] != prev->kind[j])
675  {
676  is_distinct = true;
677  break;
678  }
679 
680  /*
681  * If the bounds are both MINVALUE or MAXVALUE, stop now and treat
682  * them as equal, since any values after this point must be
683  * ignored.
684  */
685  if (cur->kind[j] != PARTITION_RANGE_DATUM_VALUE)
686  break;
687 
688  cmpval = FunctionCall2Coll(&key->partsupfunc[j],
689  key->partcollation[j],
690  cur->datums[j],
691  prev->datums[j]);
692  if (DatumGetInt32(cmpval) != 0)
693  {
694  is_distinct = true;
695  break;
696  }
697  }
698 
699  /*
700  * Only if the bound is distinct save it into a temporary array, i.e,
701  * rbounds which is later copied into boundinfo datums array.
702  */
703  if (is_distinct)
704  rbounds[k++] = all_bounds[i];
705 
706  prev = cur;
707  }
708 
709  /* Update ndatums to hold the count of distinct datums. */
710  ndatums = k;
711 
712  /*
713  * Add datums to boundinfo. Canonical indexes are values ranging from 0
714  * to nparts - 1, assigned in that order to each partition's upper bound.
715  * For 'datums' elements that are lower bounds, there is -1 in the
716  * 'indexes' array to signify that no partition exists for the values less
717  * than such a bound and greater than or equal to the previous upper
718  * bound.
719  */
720  boundinfo->ndatums = ndatums;
721  boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *));
722  boundinfo->kind = (PartitionRangeDatumKind **)
723  palloc(ndatums *
724  sizeof(PartitionRangeDatumKind *));
725 
726  /*
727  * For range partitioning, an additional value of -1 is stored as the last
728  * element.
729  */
730  boundinfo->indexes = (int *) palloc((ndatums + 1) * sizeof(int));
731 
732  for (i = 0; i < ndatums; i++)
733  {
734  int j;
735 
736  boundinfo->datums[i] = (Datum *) palloc(key->partnatts *
737  sizeof(Datum));
738  boundinfo->kind[i] = (PartitionRangeDatumKind *)
739  palloc(key->partnatts *
740  sizeof(PartitionRangeDatumKind));
741  for (j = 0; j < key->partnatts; j++)
742  {
743  if (rbounds[i]->kind[j] == PARTITION_RANGE_DATUM_VALUE)
744  boundinfo->datums[i][j] =
745  datumCopy(rbounds[i]->datums[j],
746  key->parttypbyval[j],
747  key->parttyplen[j]);
748  boundinfo->kind[i][j] = rbounds[i]->kind[j];
749  }
750 
751  /*
752  * There is no mapping for invalid indexes.
753  *
754  * Any lower bounds in the rbounds array have invalid indexes
755  * assigned, because the values between the previous bound (if there
756  * is one) and this (lower) bound are not part of the range of any
757  * existing partition.
758  */
759  if (rbounds[i]->lower)
760  boundinfo->indexes[i] = -1;
761  else
762  {
763  int orig_index = rbounds[i]->index;
764 
765  /* If the old index has no mapping, assign one */
766  if ((*mapping)[orig_index] == -1)
767  (*mapping)[orig_index] = next_index++;
768 
769  boundinfo->indexes[i] = (*mapping)[orig_index];
770  }
771  }
772 
773  /* Set the canonical value for default_index, if any. */
774  if (default_index != -1)
775  {
776  Assert(default_index >= 0 && (*mapping)[default_index] == -1);
777  (*mapping)[default_index] = next_index++;
778  boundinfo->default_index = (*mapping)[default_index];
779  }
780 
781  /* The extra -1 element. */
782  Assert(i == ndatums);
783  boundinfo->indexes[i] = -1;
784 
785  /* All partitions must now have been assigned canonical indexes. */
786  Assert(next_index == nparts);
787  return boundinfo;
788 }
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
PartitionRangeDatumKind * kind
Definition: partbounds.c:68
#define DatumGetInt32(X)
Definition: postgres.h:472
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:44
FmgrInfo * partsupfunc
Definition: partcache.h:35
PartitionRangeDatumKind
Definition: parsenodes.h:837
struct cursor * cur
Definition: ecpg.c:28
static int32 qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
Definition: partbounds.c:3689
Datum upper(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:75
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1152
static PartitionRangeBound * make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
Definition: partbounds.c:3308
#define ERROR
Definition: elog.h:43
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:131
void qsort_arg(void *base, size_t nel, size_t elsize, qsort_arg_comparator cmp, void *arg)
Definition: qsort_arg.c:113
Oid * partcollation
Definition: partcache.h:38
void * palloc0(Size size)
Definition: mcxt.c:981
uintptr_t Datum
Definition: postgres.h:367
bool * parttypbyval
Definition: partcache.h:44
#define Assert(condition)
Definition: c.h:746
int16 * parttyplen
Definition: partcache.h:43
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
void * palloc(Size size)
Definition: mcxt.c:950
#define elog(elevel,...)
Definition: elog.h:214
int i

◆ fix_merged_indexes()

static void fix_merged_indexes ( PartitionMap outer_map,
PartitionMap inner_map,
int  nmerged,
List merged_indexes 
)
static

Definition at line 2287 of file partbounds.c.

References Assert, PartitionMap::did_remapping, i, lfirst_int, PartitionMap::merged_indexes, PartitionMap::nparts, PartitionMap::old_indexes, palloc(), and pfree().

Referenced by merge_list_bounds().

2289 {
2290  int *new_indexes;
2291  int merged_index;
2292  int i;
2293  ListCell *lc;
2294 
2295  Assert(nmerged > 0);
2296 
2297  new_indexes = (int *) palloc(sizeof(int) * nmerged);
2298  for (i = 0; i < nmerged; i++)
2299  new_indexes[i] = -1;
2300 
2301  /* Build the mapping of old merged indexes to new merged indexes. */
2302  if (outer_map->did_remapping)
2303  {
2304  for (i = 0; i < outer_map->nparts; i++)
2305  {
2306  merged_index = outer_map->old_indexes[i];
2307  if (merged_index >= 0)
2308  new_indexes[merged_index] = outer_map->merged_indexes[i];
2309  }
2310  }
2311  if (inner_map->did_remapping)
2312  {
2313  for (i = 0; i < inner_map->nparts; i++)
2314  {
2315  merged_index = inner_map->old_indexes[i];
2316  if (merged_index >= 0)
2317  new_indexes[merged_index] = inner_map->merged_indexes[i];
2318  }
2319  }
2320 
2321  /* Fix the merged_indexes list using the mapping. */
2322  foreach(lc, merged_indexes)
2323  {
2324  merged_index = lfirst_int(lc);
2325  Assert(merged_index >= 0);
2326  if (new_indexes[merged_index] >= 0)
2327  lfirst_int(lc) = new_indexes[merged_index];
2328  }
2329 
2330  pfree(new_indexes);
2331 }
bool did_remapping
Definition: partbounds.c:82
void pfree(void *pointer)
Definition: mcxt.c:1057
#define lfirst_int(lc)
Definition: pg_list.h:170
#define Assert(condition)
Definition: c.h:746
void * palloc(Size size)
Definition: mcxt.c:950
int * old_indexes
Definition: partbounds.c:83
int i
int * merged_indexes
Definition: partbounds.c:79

◆ free_partition_map()

static void free_partition_map ( PartitionMap map)
static

Definition at line 1734 of file partbounds.c.

References PartitionMap::merged, PartitionMap::merged_indexes, PartitionMap::old_indexes, and pfree().

Referenced by merge_list_bounds(), and merge_range_bounds().

1735 {
1736  pfree(map->merged_indexes);
1737  pfree(map->merged);
1738  pfree(map->old_indexes);
1739 }
void pfree(void *pointer)
Definition: mcxt.c:1057
bool * merged
Definition: partbounds.c:80
int * old_indexes
Definition: partbounds.c:83
int * merged_indexes
Definition: partbounds.c:79

◆ generate_matching_part_pairs()

static void generate_matching_part_pairs ( RelOptInfo outer_rel,
RelOptInfo inner_rel,
PartitionMap outer_map,
PartitionMap inner_map,
int  nmerged,
List **  outer_parts,
List **  inner_parts 
)
static

Definition at line 2341 of file partbounds.c.

References Assert, i, lappend(), Max, PartitionMap::merged_indexes, NIL, PartitionMap::nparts, RelOptInfo::nparts, palloc(), RelOptInfo::part_rels, and pfree().

Referenced by merge_list_bounds(), and merge_range_bounds().

2345 {
2346  int outer_nparts = outer_map->nparts;
2347  int inner_nparts = inner_map->nparts;
2348  int *outer_indexes;
2349  int *inner_indexes;
2350  int max_nparts;
2351  int i;
2352 
2353  Assert(nmerged > 0);
2354  Assert(*outer_parts == NIL);
2355  Assert(*inner_parts == NIL);
2356 
2357  outer_indexes = (int *) palloc(sizeof(int) * nmerged);
2358  inner_indexes = (int *) palloc(sizeof(int) * nmerged);
2359  for (i = 0; i < nmerged; i++)
2360  outer_indexes[i] = inner_indexes[i] = -1;
2361 
2362  /* Set pairs of matching partitions. */
2363  Assert(outer_nparts == outer_rel->nparts);
2364  Assert(inner_nparts == inner_rel->nparts);
2365  max_nparts = Max(outer_nparts, inner_nparts);
2366  for (i = 0; i < max_nparts; i++)
2367  {
2368  if (i < outer_nparts)
2369  {
2370  int merged_index = outer_map->merged_indexes[i];
2371 
2372  if (merged_index >= 0)
2373  {
2374  Assert(merged_index < nmerged);
2375  outer_indexes[merged_index] = i;
2376  }
2377  }
2378  if (i < inner_nparts)
2379  {
2380  int merged_index = inner_map->merged_indexes[i];
2381 
2382  if (merged_index >= 0)
2383  {
2384  Assert(merged_index < nmerged);
2385  inner_indexes[merged_index] = i;
2386  }
2387  }
2388  }
2389 
2390  /* Build the list pairs. */
2391  for (i = 0; i < nmerged; i++)
2392  {
2393  int outer_index = outer_indexes[i];
2394  int inner_index = inner_indexes[i];
2395 
2396  /*
2397  * If both partitions are dummy, it means the merged partition that
2398  * had been assigned to the outer/inner partition was removed when
2399  * re-merging the outer/inner partition in
2400  * merge_matching_partitions(); ignore the merged partition.
2401  */
2402  if (outer_index == -1 && inner_index == -1)
2403  continue;
2404 
2405  *outer_parts = lappend(*outer_parts, outer_index >= 0 ?
2406  outer_rel->part_rels[outer_index] : NULL);
2407  *inner_parts = lappend(*inner_parts, inner_index >= 0 ?
2408  inner_rel->part_rels[inner_index] : NULL);
2409  }
2410 
2411  pfree(outer_indexes);
2412  pfree(inner_indexes);
2413 }
#define NIL
Definition: pg_list.h:65
void pfree(void *pointer)
Definition: mcxt.c:1057
int nparts
Definition: pathnodes.h:742
List * lappend(List *list, void *datum)
Definition: list.c:321
#define Max(x, y)
Definition: c.h:922
#define Assert(condition)
Definition: c.h:746
struct RelOptInfo ** part_rels
Definition: pathnodes.h:749
void * palloc(Size size)
Definition: mcxt.c:950
int i
int * merged_indexes
Definition: partbounds.c:79

◆ get_hash_partition_greatest_modulus()

int get_hash_partition_greatest_modulus ( PartitionBoundInfo  bound)

Definition at line 3291 of file partbounds.c.

References Assert, DatumGetInt32, PartitionBoundInfoData::datums, PartitionBoundInfoData::ndatums, PARTITION_STRATEGY_HASH, and PartitionBoundInfoData::strategy.

Referenced by check_new_partition_bound(), get_matching_hash_bounds(), get_partition_bound_num_indexes(), get_partition_for_tuple(), and partition_bounds_equal().

3292 {
3293  Assert(bound && bound->strategy == PARTITION_STRATEGY_HASH);
3294  Assert(bound->datums && bound->ndatums > 0);
3295  Assert(DatumGetInt32(bound->datums[bound->ndatums - 1][0]) > 0);
3296 
3297  return DatumGetInt32(bound->datums[bound->ndatums - 1][0]);
3298 }
#define DatumGetInt32(X)
Definition: postgres.h:472
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
#define Assert(condition)
Definition: c.h:746

◆ get_merged_range_bounds()

static void get_merged_range_bounds ( int  partnatts,
FmgrInfo partsupfuncs,
Oid partcollations,
JoinType  jointype,
PartitionRangeBound outer_lb,
PartitionRangeBound outer_ub,
PartitionRangeBound inner_lb,
PartitionRangeBound inner_ub,
int  lb_cmpval,
int  ub_cmpval,
PartitionRangeBound merged_lb,
PartitionRangeBound merged_ub 
)
static

Definition at line 2609 of file partbounds.c.

References Assert, compare_range_bounds, elog, ERROR, JOIN_ANTI, JOIN_FULL, JOIN_INNER, JOIN_LEFT, and JOIN_SEMI.

Referenced by merge_range_bounds().

2618 {
2619  Assert(compare_range_bounds(partnatts, partsupfuncs, partcollations,
2620  outer_lb, inner_lb) == lb_cmpval);
2621  Assert(compare_range_bounds(partnatts, partsupfuncs, partcollations,
2622  outer_ub, inner_ub) == ub_cmpval);
2623 
2624  switch (jointype)
2625  {
2626  case JOIN_INNER:
2627  case JOIN_SEMI:
2628 
2629  /*
2630  * An INNER/SEMI join will have the rows that fit both sides, so
2631  * the lower bound of the merged partition will be the higher of
2632  * the two lower bounds, and the upper bound of the merged
2633  * partition will be the lower of the two upper bounds.
2634  */
2635  *merged_lb = (lb_cmpval > 0) ? *outer_lb : *inner_lb;
2636  *merged_ub = (ub_cmpval < 0) ? *outer_ub : *inner_ub;
2637  break;
2638 
2639  case JOIN_LEFT:
2640  case JOIN_ANTI:
2641 
2642  /*
2643  * A LEFT/ANTI join will have all the rows from the outer side, so
2644  * the bounds of the merged partition will be the same as the
2645  * outer bounds.
2646  */
2647  *merged_lb = *outer_lb;
2648  *merged_ub = *outer_ub;
2649  break;
2650 
2651  case JOIN_FULL:
2652 
2653  /*
2654  * A FULL join will have all the rows from both sides, so the
2655  * lower bound of the merged partition will be the lower of the
2656  * two lower bounds, and the upper bound of the merged partition
2657  * will be the higher of the two upper bounds.
2658  */
2659  *merged_lb = (lb_cmpval < 0) ? *outer_lb : *inner_lb;
2660  *merged_ub = (ub_cmpval > 0) ? *outer_ub : *inner_ub;
2661  break;
2662 
2663  default:
2664  elog(ERROR, "unrecognized join type: %d", (int) jointype);
2665  }
2666 }
#define ERROR
Definition: elog.h:43
#define Assert(condition)
Definition: c.h:746
#define elog(elevel,...)
Definition: elog.h:214
#define compare_range_bounds(partnatts, partsupfunc, partcollations, bound1, bound2)
Definition: partbounds.c:88

◆ get_partition_bound_num_indexes()

static int get_partition_bound_num_indexes ( PartitionBoundInfo  b)
static

Definition at line 3706 of file partbounds.c.

References Assert, elog, ERROR, get_hash_partition_greatest_modulus(), PartitionBoundInfoData::ndatums, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, and PartitionBoundInfoData::strategy.

Referenced by partition_bounds_copy().

3707 {
3708  int num_indexes;
3709 
3710  Assert(bound);
3711 
3712  switch (bound->strategy)
3713  {
3715 
3716  /*
3717  * The number of the entries in the indexes array is same as the
3718  * greatest modulus.
3719  */
3720  num_indexes = get_hash_partition_greatest_modulus(bound);
3721  break;
3722 
3724  num_indexes = bound->ndatums;
3725  break;
3726 
3728  /* Range partitioned table has an extra index. */
3729  num_indexes = bound->ndatums + 1;
3730  break;
3731 
3732  default:
3733  elog(ERROR, "unexpected partition strategy: %d",
3734  (int) bound->strategy);
3735  }
3736 
3737  return num_indexes;
3738 }
#define ERROR
Definition: elog.h:43
int get_hash_partition_greatest_modulus(PartitionBoundInfo bound)
Definition: partbounds.c:3291
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
#define Assert(condition)
Definition: c.h:746
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
#define elog(elevel,...)
Definition: elog.h:214

◆ get_partition_operator()

static Oid get_partition_operator ( PartitionKey  key,
int  col,
StrategyNumber  strategy,
bool need_relabel 
)
static

Definition at line 3751 of file partbounds.c.

References elog, ERROR, get_opfamily_member(), OidIsValid, PartitionKeyData::partopcintype, PartitionKeyData::partopfamily, and PartitionKeyData::parttypid.

Referenced by make_partition_op_expr().

3753 {
3754  Oid operoid;
3755 
3756  /*
3757  * Get the operator in the partitioning opfamily using the opclass'
3758  * declared input type as both left- and righttype.
3759  */
3760  operoid = get_opfamily_member(key->partopfamily[col],
3761  key->partopcintype[col],
3762  key->partopcintype[col],
3763  strategy);
3764  if (!OidIsValid(operoid))
3765  elog(ERROR, "missing operator %d(%u,%u) in partition opfamily %u",
3766  strategy, key->partopcintype[col], key->partopcintype[col],
3767  key->partopfamily[col]);
3768 
3769  /*
3770  * If the partition key column is not of the same type as the operator
3771  * class and not polymorphic, tell caller to wrap the non-Const expression
3772  * in a RelabelType. This matches what parse_coerce.c does.
3773  */
3774  *need_relabel = (key->parttypid[col] != key->partopcintype[col] &&
3775  key->partopcintype[col] != RECORDOID &&
3776  !IsPolymorphicType(key->partopcintype[col]));
3777 
3778  return operoid;
3779 }
Oid * partopfamily
Definition: partcache.h:33
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:652
#define ERROR
Definition: elog.h:43
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:164
Oid * partopcintype
Definition: partcache.h:34
#define elog(elevel,...)
Definition: elog.h:214

◆ get_qual_for_hash()

static List * get_qual_for_hash ( Relation  parent,
PartitionBoundSpec spec 
)
static

Definition at line 3900 of file partbounds.c.

References generate_unaccent_rules::args, COERCE_EXPLICIT_CALL, copyObject, i, Int32GetDatum, InvalidOid, sort-test::key, lappend(), lfirst, list_head(), list_make1, list_make3, lnext(), makeConst(), makeFuncExpr(), makeVar(), PartitionBoundSpec::modulus, ObjectIdGetDatum, PartitionKeyData::partattrs, PartitionKeyData::partexprs, PartitionKeyData::partnatts, PartitionKeyData::parttypcoll, PartitionKeyData::parttypid, PartitionKeyData::parttypmod, RelationGetPartitionKey(), RelationGetRelid, and PartitionBoundSpec::remainder.

Referenced by get_qual_from_partbound().

3901 {
3903  FuncExpr *fexpr;
3904  Node *relidConst;
3905  Node *modulusConst;
3906  Node *remainderConst;
3907  List *args;
3908  ListCell *partexprs_item;
3909  int i;
3910 
3911  /* Fixed arguments. */
3912  relidConst = (Node *) makeConst(OIDOID,
3913  -1,
3914  InvalidOid,
3915  sizeof(Oid),
3917  false,
3918  true);
3919 
3920  modulusConst = (Node *) makeConst(INT4OID,
3921  -1,
3922  InvalidOid,
3923  sizeof(int32),
3924  Int32GetDatum(spec->modulus),
3925  false,
3926  true);
3927 
3928  remainderConst = (Node *) makeConst(INT4OID,
3929  -1,
3930  InvalidOid,
3931  sizeof(int32),
3932  Int32GetDatum(spec->remainder),
3933  false,
3934  true);
3935 
3936  args = list_make3(relidConst, modulusConst, remainderConst);
3937  partexprs_item = list_head(key->partexprs);
3938 
3939  /* Add an argument for each key column. */
3940  for (i = 0; i < key->partnatts; i++)
3941  {
3942  Node *keyCol;
3943 
3944  /* Left operand */
3945  if (key->partattrs[i] != 0)
3946  {
3947  keyCol = (Node *) makeVar(1,
3948  key->partattrs[i],
3949  key->parttypid[i],
3950  key->parttypmod[i],
3951  key->parttypcoll[i],
3952  0);
3953  }
3954  else
3955  {
3956  keyCol = (Node *) copyObject(lfirst(partexprs_item));
3957  partexprs_item = lnext(key->partexprs, partexprs_item);
3958  }
3959 
3960  args = lappend(args, keyCol);
3961  }
3962 
3963  fexpr = makeFuncExpr(F_SATISFIES_HASH_PARTITION,
3964  BOOLOID,
3965  args,
3966  InvalidOid,
3967  InvalidOid,
3969 
3970  return list_make1(fexpr);
3971 }
#define list_make3(x1, x2, x3)
Definition: pg_list.h:210
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:310
Definition: nodes.h:528
List * partexprs
Definition: partcache.h:30
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
unsigned int Oid
Definition: postgres_ext.h:31
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:299
signed int int32
Definition: c.h:363
#define list_make1(x1)
Definition: pg_list.h:206
Oid * parttypcoll
Definition: partcache.h:46
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
List * lappend(List *list, void *datum)
Definition: list.c:321
AttrNumber * partattrs
Definition: partcache.h:28
int32 * parttypmod
Definition: partcache.h:42
#define InvalidOid
Definition: postgres_ext.h:36
#define lfirst(lc)
Definition: pg_list.h:169
#define Int32GetDatum(X)
Definition: postgres.h:479
int i
#define copyObject(obj)
Definition: nodes.h:644
Definition: pg_list.h:50
#define RelationGetRelid(relation)
Definition: rel.h:456
FuncExpr * makeFuncExpr(Oid funcid, Oid rettype, List *args, Oid funccollid, Oid inputcollid, CoercionForm fformat)
Definition: makefuncs.c:519

◆ get_qual_for_list()

static List * get_qual_for_list ( Relation  parent,
PartitionBoundSpec spec 
)
static

Definition at line 3983 of file partbounds.c.

References NullTest::arg, NullTest::argisrow, Assert, PartitionDescData::boundinfo, BTEqualStrategyNumber, castNode, Const::constisnull, copyObject, datumCopy(), PartitionBoundInfoData::datums, i, PartitionBoundSpec::is_default, IS_NOT_NULL, IS_NULL, sort-test::key, lappend(), lfirst, linitial, list_make1, list_make2, PartitionBoundSpec::listdatums, NullTest::location, make_ands_explicit(), make_partition_op_expr(), makeBoolExpr(), makeConst(), makeNode, makeVar(), PartitionBoundInfoData::ndatums, NIL, NOT_EXPR, NullTest::nulltesttype, OR_EXPR, PartitionKeyData::partattrs, PartitionKeyData::partexprs, partition_bound_accepts_nulls, PartitionKeyData::partnatts, PartitionKeyData::parttypbyval, PartitionKeyData::parttypcoll, PartitionKeyData::parttypid, PartitionKeyData::parttyplen, PartitionKeyData::parttypmod, RelationGetPartitionDesc(), RelationGetPartitionKey(), and val.

Referenced by check_default_partition_contents(), and get_qual_from_partbound().

3984 {
3986  List *result;
3987  Expr *keyCol;
3988  Expr *opexpr;
3989  NullTest *nulltest;
3990  ListCell *cell;
3991  List *elems = NIL;
3992  bool list_has_null = false;
3993 
3994  /*
3995  * Only single-column list partitioning is supported, so we are worried
3996  * only about the partition key with index 0.
3997  */
3998  Assert(key->partnatts == 1);
3999 
4000  /* Construct Var or expression representing the partition column */
4001  if (key->partattrs[0] != 0)
4002  keyCol = (Expr *) makeVar(1,
4003  key->partattrs[0],
4004  key->parttypid[0],
4005  key->parttypmod[0],
4006  key->parttypcoll[0],
4007  0);
4008  else
4009  keyCol = (Expr *) copyObject(linitial(key->partexprs));
4010 
4011  /*
4012  * For default list partition, collect datums for all the partitions. The
4013  * default partition constraint should check that the partition key is
4014  * equal to none of those.
4015  */
4016  if (spec->is_default)
4017  {
4018  int i;
4019  int ndatums = 0;
4020  PartitionDesc pdesc = RelationGetPartitionDesc(parent);
4021  PartitionBoundInfo boundinfo = pdesc->boundinfo;
4022 
4023  if (boundinfo)
4024  {
4025  ndatums = boundinfo->ndatums;
4026 
4027  if (partition_bound_accepts_nulls(boundinfo))
4028  list_has_null = true;
4029  }
4030 
4031  /*
4032  * If default is the only partition, there need not be any partition
4033  * constraint on it.
4034  */
4035  if (ndatums == 0 && !list_has_null)
4036  return NIL;
4037 
4038  for (i = 0; i < ndatums; i++)
4039  {
4040  Const *val;
4041 
4042  /*
4043  * Construct Const from known-not-null datum. We must be careful
4044  * to copy the value, because our result has to be able to outlive
4045  * the relcache entry we're copying from.
4046  */
4047  val = makeConst(key->parttypid[0],
4048  key->parttypmod[0],
4049  key->parttypcoll[0],
4050  key->parttyplen[0],
4051  datumCopy(*boundinfo->datums[i],
4052  key->parttypbyval[0],
4053  key->parttyplen[0]),
4054  false, /* isnull */
4055  key->parttypbyval[0]);
4056 
4057  elems = lappend(elems, val);
4058  }
4059  }
4060  else
4061  {
4062  /*
4063  * Create list of Consts for the allowed values, excluding any nulls.
4064  */
4065  foreach(cell, spec->listdatums)
4066  {
4067  Const *val = castNode(Const, lfirst(cell));
4068 
4069  if (val->constisnull)
4070  list_has_null = true;
4071  else
4072  elems = lappend(elems, copyObject(val));
4073  }
4074  }
4075 
4076  if (elems)
4077  {
4078  /*
4079  * Generate the operator expression from the non-null partition
4080  * values.
4081  */
4083  keyCol, (Expr *) elems);
4084  }
4085  else
4086  {
4087  /*
4088  * If there are no partition values, we don't need an operator
4089  * expression.
4090  */
4091  opexpr = NULL;
4092  }
4093 
4094  if (!list_has_null)
4095  {
4096  /*
4097  * Gin up a "col IS NOT NULL" test that will be AND'd with the main
4098  * expression. This might seem redundant, but the partition routing
4099  * machinery needs it.
4100  */
4101  nulltest = makeNode(NullTest);
4102  nulltest->arg = keyCol;
4103  nulltest->nulltesttype = IS_NOT_NULL;
4104  nulltest->argisrow = false;
4105  nulltest->location = -1;
4106 
4107  result = opexpr ? list_make2(nulltest, opexpr) : list_make1(nulltest);
4108  }
4109  else
4110  {
4111  /*
4112  * Gin up a "col IS NULL" test that will be OR'd with the main
4113  * expression.
4114  */
4115  nulltest = makeNode(NullTest);
4116  nulltest->arg = keyCol;
4117  nulltest->nulltesttype = IS_NULL;
4118  nulltest->argisrow = false;
4119  nulltest->location = -1;
4120 
4121  if (opexpr)
4122  {
4123  Expr *or;
4124 
4125  or = makeBoolExpr(OR_EXPR, list_make2(nulltest, opexpr), -1);
4126  result = list_make1(or);
4127  }
4128  else
4129  result = list_make1(nulltest);
4130  }
4131 
4132  /*
4133  * Note that, in general, applying NOT to a constraint expression doesn't
4134  * necessarily invert the set of rows it accepts, because NOT (NULL) is
4135  * NULL. However, the partition constraints we construct here never
4136  * evaluate to NULL, so applying NOT works as intended.
4137  */
4138  if (spec->is_default)
4139  {
4140  result = list_make1(make_ands_explicit(result));
4141  result = list_make1(makeBoolExpr(NOT_EXPR, result, -1));
4142  }
4143 
4144  return result;
4145 }
#define list_make2(x1, x2)
Definition: pg_list.h:208
#define NIL
Definition: pg_list.h:65
#define castNode(_type_, nodeptr)
Definition: nodes.h:597
List * partexprs
Definition: partcache.h:30
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:299
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:369
PartitionBoundInfo boundinfo
Definition: partdesc.h:29
#define list_make1(x1)
Definition: pg_list.h:206
static Expr * make_partition_op_expr(PartitionKey key, int keynum, uint16 strategy, Expr *arg1, Expr *arg2)
Definition: partbounds.c:3787
Oid * parttypcoll
Definition: partcache.h:46
#define linitial(l)
Definition: pg_list.h:174
Expr * arg
Definition: primnodes.h:1222
PartitionDesc RelationGetPartitionDesc(Relation rel)
Definition: partdesc.c:65
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:131
List * lappend(List *list, void *datum)
Definition: list.c:321
AttrNumber * partattrs
Definition: partcache.h:28
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:707
NullTestType nulltesttype
Definition: primnodes.h:1223
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:74
int32 * parttypmod
Definition: partcache.h:42
#define makeNode(_type_)
Definition: nodes.h:576
bool * parttypbyval
Definition: partcache.h:44
#define Assert(condition)
Definition: c.h:746
#define lfirst(lc)
Definition: pg_list.h:169
int16 * parttyplen
Definition: partcache.h:43
int location
Definition: primnodes.h:1225
int i
bool argisrow
Definition: primnodes.h:1224
#define copyObject(obj)
Definition: nodes.h:644
Definition: pg_list.h:50
long val
Definition: informix.c:664
bool constisnull
Definition: primnodes.h:215
#define BTEqualStrategyNumber
Definition: stratnum.h:31

◆ get_qual_for_range()

static List * get_qual_for_range ( Relation  parent,
PartitionBoundSpec spec,
bool  for_default 
)
static

Definition at line 4192 of file partbounds.c.

References AND_EXPR, BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, castNode, CreateExecutorState(), DatumGetBool, elog, ERROR, EState::es_query_cxt, ExecEvalExprSwitchContext(), ExecInitExpr(), fix_opfuncids(), for_both_cell, forboth, FreeExecutorState(), get_range_key_properties(), get_range_nulltest(), GetPerTupleExprContext, HeapTupleIsValid, i, PartitionBoundSpec::is_default, IsA, sort-test::key, PartitionRangeDatum::kind, lappend(), lfirst, linitial, list_head(), list_length(), list_make1, lnext(), PartitionBoundSpec::lowerdatums, make_partition_op_expr(), makeBoolConst(), makeBoolExpr(), MemoryContextSwitchTo(), NIL, NOT_EXPR, PartitionDescData::nparts, PartitionDescData::oids, OR_EXPR, PartitionKeyData::partexprs, PARTITION_RANGE_DATUM_MAXVALUE, PARTITION_RANGE_DATUM_MINVALUE, PARTITION_RANGE_DATUM_VALUE, PartitionKeyData::partnatts, RelationGetPartitionDesc(), RelationGetPartitionKey(), ReleaseSysCache(), RELOID, SearchSysCache1(), stringToNode(), SysCacheGetAttr(), TextDatumGetCString, and PartitionBoundSpec::upperdatums.

Referenced by check_default_partition_contents(), and get_qual_from_partbound().

4194 {
4195  List *result = NIL;
4196  ListCell *cell1,
4197  *cell2,
4198  *partexprs_item,
4199  *partexprs_item_saved;
4200  int i,
4201  j;
4202  PartitionRangeDatum *ldatum,
4203  *udatum;
4205  Expr *keyCol;
4206  Const *lower_val,
4207  *upper_val;
4208  List *lower_or_arms,
4209  *upper_or_arms;
4210  int num_or_arms,
4211  current_or_arm;
4212  ListCell *lower_or_start_datum,
4213  *upper_or_start_datum;
4214  bool need_next_lower_arm,
4215  need_next_upper_arm;
4216 
4217  if (spec->is_default)
4218  {
4219  List *or_expr_args = NIL;
4220  PartitionDesc pdesc = RelationGetPartitionDesc(parent);
4221  Oid *inhoids = pdesc->oids;
4222  int nparts = pdesc->nparts,
4223  i;
4224 
4225  for (i = 0; i < nparts; i++)
4226  {
4227  Oid inhrelid = inhoids[i];
4228  HeapTuple tuple;
4229  Datum datum;
4230  bool isnull;
4231  PartitionBoundSpec *bspec;
4232 
4233  tuple = SearchSysCache1(RELOID, inhrelid);
4234  if (!HeapTupleIsValid(tuple))
4235  elog(ERROR, "cache lookup failed for relation %u", inhrelid);
4236 
4237  datum = SysCacheGetAttr(RELOID, tuple,
4238  Anum_pg_class_relpartbound,
4239  &isnull);
4240  if (isnull)
4241  elog(ERROR, "null relpartbound for relation %u", inhrelid);
4242 
4243  bspec = (PartitionBoundSpec *)
4245  if (!IsA(bspec, PartitionBoundSpec))
4246  elog(ERROR, "expected PartitionBoundSpec");
4247 
4248  if (!bspec->is_default)
4249  {
4250  List *part_qual;
4251 
4252  part_qual = get_qual_for_range(parent, bspec, true);
4253 
4254  /*
4255  * AND the constraints of the partition and add to
4256  * or_expr_args
4257  */
4258  or_expr_args = lappend(or_expr_args, list_length(part_qual) > 1
4259  ? makeBoolExpr(AND_EXPR, part_qual, -1)
4260  : linitial(part_qual));
4261  }
4262  ReleaseSysCache(tuple);
4263  }
4264 
4265  if (or_expr_args != NIL)
4266  {
4267  Expr *other_parts_constr;
4268 
4269  /*
4270  * Combine the constraints obtained for non-default partitions
4271  * using OR. As requested, each of the OR's args doesn't include
4272  * the NOT NULL test for partition keys (which is to avoid its
4273  * useless repetition). Add the same now.
4274  */
4275  other_parts_constr =
4278  list_length(or_expr_args) > 1
4279  ? makeBoolExpr(OR_EXPR, or_expr_args,
4280  -1)
4281  : linitial(or_expr_args)),
4282  -1);
4283 
4284  /*
4285  * Finally, the default partition contains everything *NOT*
4286  * contained in the non-default partitions.
4287  */
4288  result = list_make1(makeBoolExpr(NOT_EXPR,
4289  list_make1(other_parts_constr), -1));
4290  }
4291 
4292  return result;
4293  }
4294 
4295  /*
4296  * If it is the recursive call for default, we skip the get_range_nulltest
4297  * to avoid accumulating the NullTest on the same keys for each partition.
4298  */
4299  if (!for_default)
4300  result = get_range_nulltest(key);
4301 
4302  /*
4303  * Iterate over the key columns and check if the corresponding lower and
4304  * upper datums are equal using the btree equality operator for the
4305  * column's type. If equal, we emit single keyCol = common_value
4306  * expression. Starting from the first column for which the corresponding
4307  * lower and upper bound datums are not equal, we generate OR expressions
4308  * as shown in the function's header comment.
4309  */
4310  i = 0;
4311  partexprs_item = list_head(key->partexprs);
4312  partexprs_item_saved = partexprs_item; /* placate compiler */
4313  forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
4314  {
4315  EState *estate;
4316  MemoryContext oldcxt;
4317  Expr *test_expr;
4318  ExprState *test_exprstate;
4319  Datum test_result;
4320  bool isNull;
4321 
4322  ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
4323  udatum = castNode(PartitionRangeDatum, lfirst(cell2));
4324 
4325  /*
4326  * Since get_range_key_properties() modifies partexprs_item, and we
4327  * might need to start over from the previous expression in the later
4328  * part of this function, save away the current value.
4329  */
4330  partexprs_item_saved = partexprs_item;
4331 
4332  get_range_key_properties(key, i, ldatum, udatum,
4333  &partexprs_item,
4334  &keyCol,
4335  &lower_val, &upper_val);
4336 
4337  /*
4338  * If either value is NULL, the corresponding partition bound is
4339  * either MINVALUE or MAXVALUE, and we treat them as unequal, because
4340  * even if they're the same, there is no common value to equate the
4341  * key column with.
4342  */
4343  if (!lower_val || !upper_val)
4344  break;
4345 
4346  /* Create the test expression */
4347  estate = CreateExecutorState();
4348  oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
4349  test_expr = make_partition_op_expr(key, i, BTEqualStrategyNumber,
4350  (Expr *) lower_val,
4351  (Expr *) upper_val);
4352  fix_opfuncids((Node *) test_expr);
4353  test_exprstate = ExecInitExpr(test_expr, NULL);
4354  test_result = ExecEvalExprSwitchContext(test_exprstate,
4355  GetPerTupleExprContext(estate),
4356  &isNull);
4357  MemoryContextSwitchTo(oldcxt);
4358  FreeExecutorState(estate);
4359 
4360  /* If not equal, go generate the OR expressions */
4361  if (!DatumGetBool(test_result))
4362  break;
4363 
4364  /*
4365  * The bounds for the last key column can't be equal, because such a
4366  * range partition would never be allowed to be defined (it would have
4367  * an empty range otherwise).
4368  */
4369  if (i == key->partnatts - 1)
4370  elog(ERROR, "invalid range bound specification");
4371 
4372  /* Equal, so generate keyCol = lower_val expression */
4373  result = lappend(result,
4375  keyCol, (Expr *) lower_val));
4376 
4377  i++;
4378  }
4379 
4380  /* First pair of lower_val and upper_val that are not equal. */
4381  lower_or_start_datum = cell1;
4382  upper_or_start_datum = cell2;
4383 
4384  /* OR will have as many arms as there are key columns left. */
4385  num_or_arms = key->partnatts - i;
4386  current_or_arm = 0;
4387  lower_or_arms = upper_or_arms = NIL;
4388  need_next_lower_arm = need_next_upper_arm = true;
4389  while (current_or_arm < num_or_arms)
4390  {
4391  List *lower_or_arm_args = NIL,
4392  *upper_or_arm_args = NIL;
4393 
4394  /* Restart scan of columns from the i'th one */
4395  j = i;
4396  partexprs_item = partexprs_item_saved;
4397 
4398  for_both_cell(cell1, spec->lowerdatums, lower_or_start_datum,
4399  cell2, spec->upperdatums, upper_or_start_datum)
4400  {
4401  PartitionRangeDatum *ldatum_next = NULL,
4402  *udatum_next = NULL;
4403 
4404  ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
4405  if (lnext(spec->lowerdatums, cell1))
4406  ldatum_next = castNode(PartitionRangeDatum,
4407  lfirst(lnext(spec->lowerdatums, cell1)));
4408  udatum = castNode(PartitionRangeDatum, lfirst(cell2));
4409  if (lnext(spec->upperdatums, cell2))
4410  udatum_next = castNode(PartitionRangeDatum,
4411  lfirst(lnext(spec->upperdatums, cell2)));
4412  get_range_key_properties(key, j, ldatum, udatum,
4413  &partexprs_item,
4414  &keyCol,
4415  &lower_val, &upper_val);
4416 
4417  if (need_next_lower_arm && lower_val)
4418  {
4419  uint16 strategy;
4420 
4421  /*
4422  * For the non-last columns of this arm, use the EQ operator.
4423  * For the last column of this arm, use GT, unless this is the
4424  * last column of the whole bound check, or the next bound
4425  * datum is MINVALUE, in which case use GE.
4426  */
4427  if (j - i < current_or_arm)
4428  strategy = BTEqualStrategyNumber;
4429  else if (j == key->partnatts - 1 ||
4430  (ldatum_next &&
4431  ldatum_next->kind == PARTITION_RANGE_DATUM_MINVALUE))
4432  strategy = BTGreaterEqualStrategyNumber;
4433  else
4434  strategy = BTGreaterStrategyNumber;
4435 
4436  lower_or_arm_args = lappend(lower_or_arm_args,
4437  make_partition_op_expr(key, j,
4438  strategy,
4439  keyCol,
4440  (Expr *) lower_val));
4441  }
4442 
4443  if (need_next_upper_arm && upper_val)
4444  {
4445  uint16 strategy;
4446 
4447  /*
4448  * For the non-last columns of this arm, use the EQ operator.
4449  * For the last column of this arm, use LT, unless the next
4450  * bound datum is MAXVALUE, in which case use LE.
4451  */
4452  if (j - i < current_or_arm)
4453  strategy = BTEqualStrategyNumber;
4454  else if (udatum_next &&
4455  udatum_next->kind == PARTITION_RANGE_DATUM_MAXVALUE)
4456  strategy = BTLessEqualStrategyNumber;
4457  else
4458  strategy = BTLessStrategyNumber;
4459 
4460  upper_or_arm_args = lappend(upper_or_arm_args,
4461  make_partition_op_expr(key, j,
4462  strategy,
4463  keyCol,
4464  (Expr *) upper_val));
4465  }
4466 
4467  /*
4468  * Did we generate enough of OR's arguments? First arm considers
4469  * the first of the remaining columns, second arm considers first
4470  * two of the remaining columns, and so on.
4471  */
4472  ++j;
4473  if (j - i > current_or_arm)
4474  {
4475  /*
4476  * We must not emit any more arms if the new column that will
4477  * be considered is unbounded, or this one was.
4478  */
4479  if (!lower_val || !ldatum_next ||
4480  ldatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
4481  need_next_lower_arm = false;
4482  if (!upper_val || !udatum_next ||
4483  udatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
4484  need_next_upper_arm = false;
4485  break;
4486  }
4487  }
4488 
4489  if (lower_or_arm_args != NIL)
4490  lower_or_arms = lappend(lower_or_arms,
4491  list_length(lower_or_arm_args) > 1
4492  ? makeBoolExpr(AND_EXPR, lower_or_arm_args, -1)
4493  : linitial(lower_or_arm_args));
4494 
4495  if (upper_or_arm_args != NIL)
4496  upper_or_arms = lappend(upper_or_arms,
4497  list_length(upper_or_arm_args) > 1
4498  ? makeBoolExpr(AND_EXPR, upper_or_arm_args, -1)
4499  : linitial(upper_or_arm_args));
4500 
4501  /* If no work to do in the next iteration, break away. */
4502  if (!need_next_lower_arm && !need_next_upper_arm)
4503  break;
4504 
4505  ++current_or_arm;
4506  }
4507 
4508  /*
4509  * Generate the OR expressions for each of lower and upper bounds (if
4510  * required), and append to the list of implicitly ANDed list of
4511  * expressions.
4512  */
4513  if (lower_or_arms != NIL)
4514  result = lappend(result,
4515  list_length(lower_or_arms) > 1
4516  ? makeBoolExpr(OR_EXPR, lower_or_arms, -1)
4517  : linitial(lower_or_arms));
4518  if (upper_or_arms != NIL)
4519  result = lappend(result,
4520  list_length(upper_or_arms) > 1
4521  ? makeBoolExpr(OR_EXPR, upper_or_arms, -1)
4522  : linitial(upper_or_arms));
4523 
4524  /*
4525  * As noted above, for non-default, we return list with constant TRUE. If
4526  * the result is NIL during the recursive call for default, it implies
4527  * this is the only other partition which can hold every value of the key
4528  * except NULL. Hence we return the NullTest result skipped earlier.
4529  */
4530  if (result == NIL)
4531  result = for_default
4532  ? get_range_nulltest(key)
4534 
4535  return result;
4536 }
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:579
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:304
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:434
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:310
#define castNode(_type_, nodeptr)
Definition: nodes.h:597
void fix_opfuncids(Node *node)
Definition: nodeFuncs.c:1636
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
PartitionRangeDatumKind kind
Definition: parsenodes.h:848
Definition: nodes.h:528
static List * get_range_nulltest(PartitionKey key)
Definition: partbounds.c:4598
void * stringToNode(const char *str)
Definition: read.c:89
#define false
Definition: c.h:333
List * partexprs
Definition: partcache.h:30
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
unsigned int Oid
Definition: postgres_ext.h:31
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:369
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
#define list_make1(x1)
Definition: pg_list.h:206
void FreeExecutorState(EState *estate)
Definition: execUtils.c:185
static Expr * make_partition_op_expr(PartitionKey key, int keynum, uint16 strategy, Expr *arg1, Expr *arg2)
Definition: partbounds.c:3787
static void get_range_key_properties(PartitionKey key, int keynum, PartitionRangeDatum *ldatum, PartitionRangeDatum *udatum, ListCell **partexprs_item, Expr **keyCol, Const **lower_val, Const **upper_val)
Definition: partbounds.c:4554
#define GetPerTupleExprContext(estate)
Definition: executor.h:506
#define true
Definition: c.h:329
unsigned short uint16
Definition: c.h:374
MemoryContext es_query_cxt
Definition: execnodes.h:559
#define linitial(l)
Definition: pg_list.h:174
#define ERROR
Definition: elog.h:43
Node * makeBoolConst(bool value, bool isnull)
Definition: makefuncs.c:357
#define DatumGetBool(X)
Definition: postgres.h:393
PartitionDesc RelationGetPartitionDesc(Relation rel)
Definition: partdesc.c:65
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
EState * CreateExecutorState(void)
Definition: execUtils.c:89
List * lappend(List *list, void *datum)
Definition: list.c:321
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1116
#define TextDatumGetCString(d)
Definition: builtins.h:87
uintptr_t Datum
Definition: postgres.h:367
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1164
#define for_both_cell(cell1, list1, initcell1, cell2, list2, initcell2)
Definition: pg_list.h:456
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1377
static List * get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default)
Definition: partbounds.c:4192
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define lfirst(lc)
Definition: pg_list.h:169
static int list_length(const List *l)
Definition: pg_list.h:149
#define elog(elevel,...)
Definition: elog.h:214
int i
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:123
#define BTLessStrategyNumber
Definition: stratnum.h:29
Definition: pg_list.h:50
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32

◆ get_qual_from_partbound()

List* get_qual_from_partbound ( Relation  rel,
Relation  parent,
PartitionBoundSpec spec 
)

Definition at line 250 of file partbounds.c.

References Assert, elog, ERROR, 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(), PartitionKeyData::strategy, and PartitionBoundSpec::strategy.

Referenced by ATExecAttachPartition(), and generate_partition_qual().

252 {
254  List *my_qual = NIL;
255 
256  Assert(key != NULL);
257 
258  switch (key->strategy)
259  {
262  my_qual = get_qual_for_hash(parent, spec);
263  break;
264 
267  my_qual = get_qual_for_list(parent, spec);
268  break;
269 
272  my_qual = get_qual_for_range(parent, spec, false);
273  break;
274 
275  default:
276  elog(ERROR, "unexpected partition strategy: %d",
277  (int) key->strategy);
278  }
279 
280  return my_qual;
281 }
#define NIL
Definition: pg_list.h:65
static List * get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:3900
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
#define ERROR
Definition: elog.h:43
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
static List * get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default)
Definition: partbounds.c:4192
#define Assert(condition)
Definition: c.h:746
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
#define elog(elevel,...)
Definition: elog.h:214
Definition: pg_list.h:50
static List * get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:3983

◆ get_range_key_properties()

static void get_range_key_properties ( PartitionKey  key,
int  keynum,
PartitionRangeDatum ldatum,
PartitionRangeDatum udatum,
ListCell **  partexprs_item,
Expr **  keyCol,
Const **  lower_val,
Const **  upper_val 
)
static

Definition at line 4554 of file partbounds.c.

References castNode, copyObject, elog, ERROR, PartitionRangeDatum::kind, lfirst, lnext(), makeVar(), PartitionKeyData::partattrs, PartitionKeyData::partexprs, PARTITION_RANGE_DATUM_VALUE, PartitionKeyData::parttypcoll, PartitionKeyData::parttypid, PartitionKeyData::parttypmod, and PartitionRangeDatum::value.

Referenced by get_qual_for_range().

4560 {
4561  /* Get partition key expression for this column */
4562  if (key->partattrs[keynum] != 0)
4563  {
4564  *keyCol = (Expr *) makeVar(1,
4565  key->partattrs[keynum],
4566  key->parttypid[keynum],
4567  key->parttypmod[keynum],
4568  key->parttypcoll[keynum],
4569  0);
4570  }
4571  else
4572  {
4573  if (*partexprs_item == NULL)
4574  elog(ERROR, "wrong number of partition key expressions");
4575  *keyCol = copyObject(lfirst(*partexprs_item));
4576  *partexprs_item = lnext(key->partexprs, *partexprs_item);
4577  }
4578 
4579  /* Get appropriate Const nodes for the bounds */
4580  if (ldatum->kind == PARTITION_RANGE_DATUM_VALUE)
4581  *lower_val = castNode(Const, copyObject(ldatum->value));
4582  else
4583  *lower_val = NULL;
4584 
4585  if (udatum->kind == PARTITION_RANGE_DATUM_VALUE)
4586  *upper_val = castNode(Const, copyObject(udatum->value));
4587  else
4588  *upper_val = NULL;
4589 }
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:310
#define castNode(_type_, nodeptr)
Definition: nodes.h:597
PartitionRangeDatumKind kind
Definition: parsenodes.h:848
List * partexprs
Definition: partcache.h:30
Oid * parttypcoll
Definition: partcache.h:46
#define ERROR
Definition: elog.h:43
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
AttrNumber * partattrs
Definition: partcache.h:28
int32 * parttypmod
Definition: partcache.h:42
#define lfirst(lc)
Definition: pg_list.h:169
#define elog(elevel,...)
Definition: elog.h:214
#define copyObject(obj)
Definition: nodes.h:644

◆ get_range_nulltest()

static List * get_range_nulltest ( PartitionKey  key)
static

Definition at line 4598 of file partbounds.c.

References NullTest::arg, NullTest::argisrow, copyObject, elog, ERROR, i, IS_NOT_NULL, lappend(), lfirst, list_head(), lnext(), NullTest::location, makeNode, makeVar(), NIL, NullTest::nulltesttype, PartitionKeyData::partattrs, PartitionKeyData::partexprs, PartitionKeyData::partnatts, PartitionKeyData::parttypcoll, PartitionKeyData::parttypid, and PartitionKeyData::parttypmod.

Referenced by get_qual_for_range().

4599 {
4600  List *result = NIL;
4601  NullTest *nulltest;
4602  ListCell *partexprs_item;
4603  int i;
4604 
4605  partexprs_item = list_head(key->partexprs);
4606  for (i = 0; i < key->partnatts; i++)
4607  {
4608  Expr *keyCol;
4609 
4610  if (key->partattrs[i] != 0)
4611  {
4612  keyCol = (Expr *) makeVar(1,
4613  key->partattrs[i],
4614  key->parttypid[i],
4615  key->parttypmod[i],
4616  key->parttypcoll[i],
4617  0);
4618  }
4619  else
4620  {
4621  if (partexprs_item == NULL)
4622  elog(ERROR, "wrong number of partition key expressions");
4623  keyCol = copyObject(lfirst(partexprs_item));
4624  partexprs_item = lnext(key->partexprs, partexprs_item);
4625  }
4626 
4627  nulltest = makeNode(NullTest);
4628  nulltest->arg = keyCol;
4629  nulltest->nulltesttype = IS_NOT_NULL;
4630  nulltest->argisrow = false;
4631  nulltest->location = -1;
4632  result = lappend(result, nulltest);
4633  }
4634 
4635  return result;
4636 }
#define NIL
Definition: pg_list.h:65
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:310
List * partexprs
Definition: partcache.h:30
Oid * parttypcoll
Definition: partcache.h:46
#define ERROR
Definition: elog.h:43
Expr * arg
Definition: primnodes.h:1222
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
List * lappend(List *list, void *datum)
Definition: list.c:321
AttrNumber * partattrs
Definition: partcache.h:28
NullTestType nulltesttype
Definition: primnodes.h:1223
int32 * parttypmod
Definition: partcache.h:42
#define makeNode(_type_)
Definition: nodes.h:576
#define lfirst(lc)
Definition: pg_list.h:169
int location
Definition: primnodes.h:1225
#define elog(elevel,...)
Definition: elog.h:214
int i
bool argisrow
Definition: primnodes.h:1224
#define copyObject(obj)
Definition: nodes.h:644
Definition: pg_list.h:50

◆ get_range_partition()

static int get_range_partition ( RelOptInfo rel,
PartitionBoundInfo  bi,
int *  lb_pos,
PartitionRangeBound lb,
PartitionRangeBound ub 
)
static

Definition at line 2479 of file partbounds.c.

References Assert, get_range_partition_internal(), is_dummy_partition(), PARTITION_STRATEGY_RANGE, and PartitionBoundInfoData::strategy.

Referenced by merge_range_bounds().

2484 {
2485  int part_index;
2486 
2488 
2489  do
2490  {
2491  part_index = get_range_partition_internal(bi, lb_pos, lb, ub);
2492  if (part_index == -1)
2493  return -1;
2494  } while (is_dummy_partition(rel, part_index));
2495 
2496  return part_index;
2497 }
static int get_range_partition_internal(PartitionBoundInfo bi, int *lb_pos, PartitionRangeBound *lb, PartitionRangeBound *ub)
Definition: partbounds.c:2500
static bool is_dummy_partition(RelOptInfo *rel, int part_index)
Definition: partbounds.c:1745
#define Assert(condition)
Definition: c.h:746
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803

◆ get_range_partition_internal()

static int get_range_partition_internal ( PartitionBoundInfo  bi,
int *  lb_pos,
PartitionRangeBound lb,
PartitionRangeBound ub 
)
static

Definition at line 2500 of file partbounds.c.

References Assert, PartitionBoundInfoData::datums, PartitionRangeBound::datums, PartitionRangeBound::index, PartitionBoundInfoData::indexes, PartitionBoundInfoData::kind, PartitionRangeBound::kind, PartitionRangeBound::lower, and PartitionBoundInfoData::ndatums.

Referenced by get_range_partition().

2504 {
2505  /* Return the index as -1 if we've exhausted all lower bounds. */
2506  if (*lb_pos >= bi->ndatums)
2507  return -1;
2508 
2509  /* A lower bound should have at least one more bound after it. */
2510  Assert(*lb_pos + 1 < bi->ndatums);
2511 
2512  /* Set the lower bound. */
2513  lb->index = bi->indexes[*lb_pos];
2514  lb->datums = bi->datums[*lb_pos];
2515  lb->kind = bi->kind[*lb_pos];
2516  lb->lower = true;
2517  /* Set the upper bound. */
2518  ub->index = bi->indexes[*lb_pos + 1];
2519  ub->datums = bi->datums[*lb_pos + 1];
2520  ub->kind = bi->kind[*lb_pos + 1];
2521  ub->lower = false;
2522 
2523  /* The index assigned to an upper bound should be valid. */
2524  Assert(ub->index >= 0);
2525 
2526  /*
2527  * Advance the position to the next lower bound. If there are no bounds
2528  * left beyond the upper bound, we have reached the last lower bound.
2529  */
2530  if (*lb_pos + 2 >= bi->ndatums)
2531  *lb_pos = bi->ndatums;
2532  else
2533  {
2534  /*
2535  * If the index assigned to the bound next to the upper bound isn't
2536  * valid, that is the next lower bound; else, the upper bound is also
2537  * the lower bound of the next range partition.
2538  */
2539  if (bi->indexes[*lb_pos + 2] < 0)
2540  *lb_pos = *lb_pos + 2;
2541  else
2542  *lb_pos = *lb_pos + 1;
2543  }
2544 
2545  return ub->index;
2546 }
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
PartitionRangeDatumKind * kind
Definition: partbounds.c:68
#define Assert(condition)
Definition: c.h:746

◆ init_partition_map()

static void init_partition_map ( RelOptInfo rel,
PartitionMap map 
)
static

Definition at line 1713 of file partbounds.c.

References PartitionMap::did_remapping, i, PartitionMap::merged, PartitionMap::merged_indexes, PartitionMap::nparts, RelOptInfo::nparts, PartitionMap::old_indexes, and palloc().

Referenced by merge_list_bounds(), and merge_range_bounds().

1714 {
1715  int nparts = rel->nparts;
1716  int i;
1717 
1718  map->nparts = nparts;
1719  map->merged_indexes = (int *) palloc(sizeof(int) * nparts);
1720  map->merged = (bool *) palloc(sizeof(bool) * nparts);
1721  map->did_remapping = false;
1722  map->old_indexes = (int *) palloc(sizeof(int) * nparts);
1723  for (i = 0; i < nparts; i++)
1724  {
1725  map->merged_indexes[i] = map->old_indexes[i] = -1;
1726  map->merged[i] = false;
1727  }
1728 }
bool did_remapping
Definition: partbounds.c:82
int nparts
Definition: pathnodes.h:742
bool * merged
Definition: partbounds.c:80
void * palloc(Size size)
Definition: mcxt.c:950
int * old_indexes
Definition: partbounds.c:83
int i
int * merged_indexes
Definition: partbounds.c:79

◆ is_dummy_partition()

static bool is_dummy_partition ( RelOptInfo rel,
int  part_index 
)
static

Definition at line 1745 of file partbounds.c.

References Assert, IS_DUMMY_REL, and RelOptInfo::part_rels.

Referenced by get_range_partition(), merge_list_bounds(), and merge_range_bounds().

1746 {
1747  RelOptInfo *part_rel;
1748 
1749  Assert(part_index >= 0);
1750  part_rel = rel->part_rels[part_index];
1751  if (part_rel == NULL || IS_DUMMY_REL(part_rel))
1752  return true;
1753  return false;
1754 }
#define IS_DUMMY_REL(r)
Definition: pathnodes.h:1417
#define Assert(condition)
Definition: c.h:746
struct RelOptInfo ** part_rels
Definition: pathnodes.h:749

◆ make_one_partition_rbound()

static PartitionRangeBound * make_one_partition_rbound ( PartitionKey  key,
int  index,
List datums,
bool  lower 
)
static

Definition at line 3308 of file partbounds.c.

References Assert, castNode, Const::constisnull, Const::constvalue, PartitionRangeBound::datums, elog, ERROR, i, PartitionHashBound::index, PartitionRangeBound::index, PartitionRangeBound::kind, PartitionRangeDatum::kind, lfirst, lower(), PartitionRangeBound::lower, NIL, palloc0(), PARTITION_RANGE_DATUM_VALUE, PartitionKeyData::partnatts, val, and PartitionRangeDatum::value.

Referenced by check_new_partition_bound(), and create_range_bounds().

3309 {
3310  PartitionRangeBound *bound;
3311  ListCell *lc;
3312  int i;
3313 
3314  Assert(datums != NIL);
3315 
3316  bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
3317  bound->index = index;
3318  bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
3319  bound->kind = (PartitionRangeDatumKind *) palloc0(key->partnatts *
3320  sizeof(PartitionRangeDatumKind));
3321  bound->lower = lower;
3322 
3323  i = 0;
3324  foreach(lc, datums)
3325  {
3327 
3328  /* What's contained in this range datum? */
3329  bound->kind[i] = datum->kind;
3330 
3331  if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
3332  {
3333  Const *val = castNode(Const, datum->value);
3334 
3335  if (val->constisnull)
3336  elog(ERROR, "invalid range bound datum");
3337  bound->datums[i] = val->constvalue;
3338  }
3339 
3340  i++;
3341  }
3342 
3343  return bound;
3344 }
Datum constvalue
Definition: primnodes.h:214
#define NIL
Definition: pg_list.h:65
PartitionRangeDatumKind * kind
Definition: partbounds.c:68
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:44
#define castNode(_type_, nodeptr)
Definition: nodes.h:597
PartitionRangeDatumKind
Definition: parsenodes.h:837
PartitionRangeDatumKind kind
Definition: parsenodes.h:848
Definition: type.h:89
#define ERROR
Definition: elog.h:43
void * palloc0(Size size)
Definition: mcxt.c:981
uintptr_t Datum
Definition: postgres.h:367
#define Assert(condition)
Definition: c.h:746
#define lfirst(lc)
Definition: pg_list.h:169
#define elog(elevel,...)
Definition: elog.h:214
int i
long val
Definition: informix.c:664
bool constisnull
Definition: primnodes.h:215

◆ make_partition_op_expr()

static Expr * make_partition_op_expr ( PartitionKey  key,
int  keynum,
uint16  strategy,
Expr arg1,
Expr arg2 
)
static

Definition at line 3787 of file partbounds.c.

References ScalarArrayOpExpr::args, ArrayExpr::array_collid, ArrayExpr::array_typeid, Assert, COERCE_EXPLICIT_CAST, ArrayExpr::element_typeid, ArrayExpr::elements, elog, ERROR, get_array_type(), get_opcode(), get_partition_operator(), ScalarArrayOpExpr::inputcollid, InvalidOid, IsA, lappend(), lfirst, linitial, list_length(), list_make2, ScalarArrayOpExpr::location, ArrayExpr::location, make_opclause(), makeBoolExpr(), makeNode, makeRelabelType(), ArrayExpr::multidims, NIL, ScalarArrayOpExpr::opfuncid, ScalarArrayOpExpr::opno, OR_EXPR, PartitionKeyData::partcollation, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionKeyData::partopcintype, PartitionKeyData::parttypcoll, PartitionKeyData::parttypid, PartitionKeyData::strategy, type_is_array, and ScalarArrayOpExpr::useOr.

Referenced by get_qual_for_list(), and get_qual_for_range().

3789 {
3790  Oid operoid;
3791  bool need_relabel = false;
3792  Expr *result = NULL;
3793 
3794  /* Get the correct btree operator for this partitioning column */
3795  operoid = get_partition_operator(key, keynum, strategy, &need_relabel);
3796 
3797  /*
3798  * Chosen operator may be such that the non-Const operand needs to be
3799  * coerced, so apply the same; see the comment in
3800  * get_partition_operator().
3801  */
3802  if (!IsA(arg1, Const) &&
3803  (need_relabel ||
3804  key->partcollation[keynum] != key->parttypcoll[keynum]))
3805  arg1 = (Expr *) makeRelabelType(arg1,
3806  key->partopcintype[keynum],
3807  -1,
3808  key->partcollation[keynum],
3810 
3811  /* Generate the actual expression */
3812  switch (key->strategy)
3813  {
3815  {
3816  List *elems = (List *) arg2;
3817  int nelems = list_length(elems);
3818 
3819  Assert(nelems >= 1);
3820  Assert(keynum == 0);
3821 
3822  if (nelems > 1 &&
3823  !type_is_array(key->parttypid[keynum]))
3824  {
3825  ArrayExpr *arrexpr;
3826  ScalarArrayOpExpr *saopexpr;
3827 
3828  /* Construct an ArrayExpr for the right-hand inputs */
3829  arrexpr = makeNode(ArrayExpr);
3830  arrexpr->array_typeid =
3831  get_array_type(key->parttypid[keynum]);
3832  arrexpr->array_collid = key->parttypcoll[keynum];
3833  arrexpr->element_typeid = key->parttypid[keynum];
3834  arrexpr->elements = elems;
3835  arrexpr->multidims = false;
3836  arrexpr->location = -1;
3837 
3838  /* Build leftop = ANY (rightop) */
3839  saopexpr = makeNode(ScalarArrayOpExpr);
3840  saopexpr->opno = operoid;
3841  saopexpr->opfuncid = get_opcode(operoid);
3842  saopexpr->useOr = true;
3843  saopexpr->inputcollid = key->partcollation[keynum];
3844  saopexpr->args = list_make2(arg1, arrexpr);
3845  saopexpr->location = -1;
3846 
3847  result = (Expr *) saopexpr;
3848  }
3849  else
3850  {
3851  List *elemops = NIL;
3852  ListCell *lc;
3853 
3854  foreach(lc, elems)
3855  {
3856  Expr *elem = lfirst(lc),
3857  *elemop;
3858 
3859  elemop = make_opclause(operoid,
3860  BOOLOID,
3861  false,
3862  arg1, elem,
3863  InvalidOid,
3864  key->partcollation[keynum]);
3865  elemops = lappend(elemops, elemop);
3866  }
3867 
3868  result = nelems > 1 ? makeBoolExpr(OR_EXPR, elemops, -1) : linitial(elemops);
3869  }
3870  break;
3871  }
3872 
3874  result = make_opclause(operoid,
3875  BOOLOID,
3876  false,
3877  arg1, arg2,
3878  InvalidOid,
3879  key->partcollation[keynum]);
3880  break;
3881 
3882  default:
3883  elog(ERROR, "invalid partitioning strategy");
3884  break;
3885  }
3886 
3887  return result;
3888 }
#define list_make2(x1, x2)
Definition: pg_list.h:208
bool multidims
Definition: primnodes.h:995
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:579
Oid get_array_type(Oid typid)
Definition: lsyscache.c:2664
Oid array_typeid
Definition: primnodes.h:991
unsigned int Oid
Definition: postgres_ext.h:31
Expr * make_opclause(Oid opno, Oid opresulttype, bool opretset, Expr *leftop, Expr *rightop, Oid opcollid, Oid inputcollid)
Definition: makefuncs.c:609
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:369
Oid * parttypcoll
Definition: partcache.h:46
#define linitial(l)
Definition: pg_list.h:174
#define ERROR
Definition: elog.h:43
RelabelType * makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod, Oid rcollid, CoercionForm rformat)
Definition: makefuncs.c:402
List * elements
Definition: primnodes.h:994
List * lappend(List *list, void *datum)
Definition: list.c:321
Oid * partcollation
Definition: partcache.h:38
int location
Definition: primnodes.h:996
static Oid get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel)
Definition: partbounds.c:3751
#define InvalidOid
Definition: postgres_ext.h:36
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1202
#define makeNode(_type_)
Definition: nodes.h:576
#define Assert(condition)
Definition: c.h:746
#define lfirst(lc)
Definition: pg_list.h:169
Oid array_collid
Definition: primnodes.h:992
static int list_length(const List *l)
Definition: pg_list.h:149
#define type_is_array(typid)
Definition: lsyscache.h:191
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
Oid element_typeid
Definition: primnodes.h:993
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
Oid * partopcintype
Definition: partcache.h:34
#define elog(elevel,...)
Definition: elog.h:214
Definition: pg_list.h:50

◆ merge_default_partitions()

static void merge_default_partitions ( PartitionMap outer_map,
PartitionMap inner_map,
bool  outer_has_default,
bool  inner_has_default,
int  outer_default,
int  inner_default,
JoinType  jointype,
int *  next_index,
int *  default_index 
)
static

Definition at line 2159 of file partbounds.c.

References Assert, IS_OUTER_JOIN, JOIN_FULL, JOIN_RIGHT, merge_matching_partitions(), merge_partition_with_dummy(), and PartitionMap::merged_indexes.

Referenced by merge_list_bounds(), and merge_range_bounds().

2168 {
2169  int outer_merged_index = -1;
2170  int inner_merged_index = -1;
2171 
2172  Assert(outer_has_default || inner_has_default);
2173 
2174  /* Get the merged partition indexes for the default partitions. */
2175  if (outer_has_default)
2176  {
2177  Assert(outer_default >= 0 && outer_default < outer_map->nparts);
2178  outer_merged_index = outer_map->merged_indexes[outer_default];
2179  }
2180  if (inner_has_default)
2181  {
2182  Assert(inner_default >= 0 && inner_default < inner_map->nparts);
2183  inner_merged_index = inner_map->merged_indexes[inner_default];
2184  }
2185 
2186  if (outer_has_default && !inner_has_default)
2187  {
2188  /*
2189  * If this is an outer join, the default partition on the outer side
2190  * has to be scanned all the way anyway; if we have not yet assigned a
2191  * partition, merge the default partition with a dummy partition on
2192  * the other side. The merged partition will act as the default
2193  * partition of the join relation (see comments in
2194  * process_inner_partition()).
2195  */
2196  if (IS_OUTER_JOIN(jointype))
2197  {
2198  Assert(jointype != JOIN_RIGHT);
2199  if (outer_merged_index == -1)
2200  {
2201  Assert(*default_index == -1);
2202  *default_index = merge_partition_with_dummy(outer_map,
2203  outer_default,
2204  next_index);
2205  }
2206  else
2207  Assert(*default_index == outer_merged_index);
2208  }
2209  else
2210  Assert(*default_index == -1);
2211  }
2212  else if (!outer_has_default && inner_has_default)
2213  {
2214  /*
2215  * If this is a FULL join, the default partition on the inner side has
2216  * to be scanned all the way anyway; if we have not yet assigned a
2217  * partition, merge the default partition with a dummy partition on
2218  * the other side. The merged partition will act as the default
2219  * partition of the join relation (see comments in
2220  * process_outer_partition()).
2221  */
2222  if (jointype == JOIN_FULL)
2223  {
2224  if (inner_merged_index == -1)
2225  {
2226  Assert(*default_index == -1);
2227  *default_index = merge_partition_with_dummy(inner_map,
2228  inner_default,
2229  next_index);
2230  }
2231  else
2232  Assert(*default_index == inner_merged_index);
2233  }
2234  else
2235  Assert(*default_index == -1);
2236  }
2237  else
2238  {
2239  Assert(outer_has_default && inner_has_default);
2240 
2241  /*
2242  * The default partitions have to be joined with each other, so merge
2243  * them. Note that each of the default partitions isn't merged yet
2244  * (see, process_outer_partition()/process_innerer_partition()), so
2245  * they should be merged successfully. The merged partition will act
2246  * as the default partition of the join relation.
2247  */
2248  Assert(outer_merged_index == -1);
2249  Assert(inner_merged_index == -1);
2250  Assert(*default_index == -1);
2251  *default_index = merge_matching_partitions(outer_map,
2252  inner_map,
2253  outer_default,
2254  inner_default,
2255  next_index);
2256  Assert(*default_index >= 0);
2257  }
2258 }
static int merge_matching_partitions(PartitionMap *outer_map, PartitionMap *inner_map, int outer_part, int inner_part, int *next_index)
Definition: partbounds.c:1764
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:744
static int merge_partition_with_dummy(PartitionMap *map, int index, int *next_index)
Definition: partbounds.c:2269
#define Assert(condition)
Definition: c.h:746
int * merged_indexes
Definition: partbounds.c:79

◆ merge_list_bounds()

static PartitionBoundInfo merge_list_bounds ( FmgrInfo partsupfunc,
Oid collations,
RelOptInfo outer_rel,
RelOptInfo inner_rel,
JoinType  jointype,
List **  outer_parts,
List **  inner_parts 
)
static

Definition at line 1100 of file partbounds.c.

References Assert, RelOptInfo::boundinfo, build_merged_partition_bounds(), cleanup(), DatumGetInt32, PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, PartitionMap::did_remapping, fix_merged_indexes(), free_partition_map(), FunctionCall2Coll(), generate_matching_part_pairs(), PartitionBoundInfoData::indexes, init_partition_map(), is_dummy_partition(), IS_OUTER_JOIN, JOIN_FULL, PartitionBoundInfoData::kind, lappend(), lappend_int(), list_free(), list_length(), merge_default_partitions(), merge_matching_partitions(), merge_null_partitions(), PartitionBoundInfoData::ndatums, NIL, PartitionBoundInfoData::null_index, partition_bound_accepts_nulls, partition_bound_has_default, PARTITION_STRATEGY_LIST, process_inner_partition(), process_outer_partition(), and PartitionBoundInfoData::strategy.

Referenced by partition_bounds_merge().

1104 {
1105  PartitionBoundInfo merged_bounds = NULL;
1106  PartitionBoundInfo outer_bi = outer_rel->boundinfo;
1107  PartitionBoundInfo inner_bi = inner_rel->boundinfo;
1108  bool outer_has_default = partition_bound_has_default(outer_bi);
1109  bool inner_has_default = partition_bound_has_default(inner_bi);
1110  int outer_default = outer_bi->default_index;
1111  int inner_default = inner_bi->default_index;
1112  bool outer_has_null = partition_bound_accepts_nulls(outer_bi);
1113  bool inner_has_null = partition_bound_accepts_nulls(inner_bi);
1114  PartitionMap outer_map;
1115  PartitionMap inner_map;
1116  int outer_pos;
1117  int inner_pos;
1118  int next_index = 0;
1119  int null_index = -1;
1120  int default_index = -1;
1121  List *merged_datums = NIL;
1122  List *merged_indexes = NIL;
1123 
1124  Assert(*outer_parts == NIL);
1125  Assert(*inner_parts == NIL);
1126  Assert(outer_bi->strategy == inner_bi->strategy &&
1127  outer_bi->strategy == PARTITION_STRATEGY_LIST);
1128  /* List partitioning doesn't require kinds. */
1129  Assert(!outer_bi->kind && !inner_bi->kind);
1130 
1131  init_partition_map(outer_rel, &outer_map);
1132  init_partition_map(inner_rel, &inner_map);
1133 
1134  /*
1135  * If the default partitions (if any) have been proven empty, deem them
1136  * non-existent.
1137  */
1138  if (outer_has_default && is_dummy_partition(outer_rel, outer_default))
1139  outer_has_default = false;
1140  if (inner_has_default && is_dummy_partition(inner_rel, inner_default))
1141  inner_has_default = false;
1142 
1143  /*
1144  * Merge partitions from both sides. In each iteration we compare a pair
1145  * of list values, one from each side, and decide whether the
1146  * corresponding partitions match or not. If the two values match
1147  * exactly, move to the next pair of list values, otherwise move to the
1148  * next list value on the side with a smaller list value.
1149  */
1150  outer_pos = inner_pos = 0;
1151  while (outer_pos < outer_bi->ndatums || inner_pos < inner_bi->ndatums)
1152  {
1153  int outer_index = -1;
1154  int inner_index = -1;
1155  Datum *outer_datums;
1156  Datum *inner_datums;
1157  int cmpval;
1158  Datum *merged_datum = NULL;
1159  int merged_index = -1;
1160 
1161  if (outer_pos < outer_bi->ndatums)
1162  {
1163  /*
1164  * If the partition on the outer side has been proven empty,
1165  * ignore it and move to the next datum on the outer side.
1166  */
1167  outer_index = outer_bi->indexes[outer_pos];
1168  if (is_dummy_partition(outer_rel, outer_index))
1169  {
1170  outer_pos++;
1171  continue;
1172  }
1173  }
1174  if (inner_pos < inner_bi->ndatums)
1175  {
1176  /*
1177  * If the partition on the inner side has been proven empty,
1178  * ignore it and move to the next datum on the inner side.
1179  */
1180  inner_index = inner_bi->indexes[inner_pos];
1181  if (is_dummy_partition(inner_rel, inner_index))
1182  {
1183  inner_pos++;
1184  continue;
1185  }
1186  }
1187 
1188  /* Get the list values. */
1189  outer_datums = outer_pos < outer_bi->ndatums ?
1190  outer_bi->datums[outer_pos] : NULL;
1191  inner_datums = inner_pos < inner_bi->ndatums ?
1192  inner_bi->datums[inner_pos] : NULL;
1193 
1194  /*
1195  * We run this loop till both sides finish. This allows us to avoid
1196  * duplicating code to handle the remaining values on the side which
1197  * finishes later. For that we set the comparison parameter cmpval in
1198  * such a way that it appears as if the side which finishes earlier
1199  * has an extra value higher than any other value on the unfinished
1200  * side. That way we advance the values on the unfinished side till
1201  * all of its values are exhausted.
1202  */
1203  if (outer_pos >= outer_bi->ndatums)
1204  cmpval = 1;
1205  else if (inner_pos >= inner_bi->ndatums)
1206  cmpval = -1;
1207  else
1208  {
1209  Assert(outer_datums != NULL && inner_datums != NULL);
1210  cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
1211  partcollation[0],
1212  outer_datums[0],
1213  inner_datums[0]));
1214  }
1215 
1216  if (cmpval == 0)
1217  {
1218  /* Two list values match exactly. */
1219  Assert(outer_pos < outer_bi->ndatums);
1220  Assert(inner_pos < inner_bi->ndatums);
1221  Assert(outer_index >= 0);
1222  Assert(inner_index >= 0);
1223 
1224  /*
1225  * Try merging both partitions. If successful, add the list value
1226  * and index of the merged partition below.
1227  */
1228  merged_index = merge_matching_partitions(&outer_map, &inner_map,
1229  outer_index, inner_index,
1230  &next_index);
1231  if (merged_index == -1)
1232  goto cleanup;
1233 
1234  merged_datum = outer_datums;
1235 
1236  /* Move to the next pair of list values. */
1237  outer_pos++;
1238  inner_pos++;
1239  }
1240  else if (cmpval < 0)
1241  {
1242  /* A list value missing from the inner side. */
1243  Assert(outer_pos < outer_bi->ndatums);
1244 
1245  /*
1246  * If the inner side has the default partition, or this is an
1247  * outer join, try to assign a merged partition to the outer
1248  * partition (see process_outer_partition()). Otherwise, the
1249  * outer partition will not contribute to the result.
1250  */
1251  if (inner_has_default || IS_OUTER_JOIN(jointype))
1252  {
1253  /* Get the outer partition. */
1254  outer_index = outer_bi->indexes[outer_pos];
1255  Assert(outer_index >= 0);
1256  merged_index = process_outer_partition(&outer_map,
1257  &inner_map,
1258  outer_has_default,
1259  inner_has_default,
1260  outer_index,
1261  inner_default,
1262  jointype,
1263  &next_index,
1264  &default_index);
1265  if (merged_index == -1)
1266  goto cleanup;
1267  merged_datum = outer_datums;
1268  }
1269 
1270  /* Move to the next list value on the outer side. */
1271  outer_pos++;
1272  }
1273  else
1274  {
1275  /* A list value missing from the outer side. */
1276  Assert(cmpval > 0);
1277  Assert(inner_pos < inner_bi->ndatums);
1278 
1279  /*
1280  * If the outer side has the default partition, or this is a FULL
1281  * join, try to assign a merged partition to the inner partition
1282  * (see process_inner_partition()). Otherwise, the inner
1283  * partition will not contribute to the result.
1284  */
1285  if (outer_has_default || jointype == JOIN_FULL)
1286  {
1287  /* Get the inner partition. */
1288  inner_index = inner_bi->indexes[inner_pos];
1289  Assert(inner_index >= 0);
1290  merged_index = process_inner_partition(&outer_map,
1291  &inner_map,
1292  outer_has_default,
1293  inner_has_default,
1294  inner_index,
1295  outer_default,
1296  jointype,
1297  &next_index,
1298  &default_index);
1299  if (merged_index == -1)
1300  goto cleanup;
1301  merged_datum = inner_datums;
1302  }
1303 
1304  /* Move to the next list value on the inner side. */
1305  inner_pos++;
1306  }
1307 
1308  /*
1309  * If we assigned a merged partition, add the list value and index of
1310  * the merged partition if appropriate.
1311  */
1312  if (merged_index >= 0 && merged_index != default_index)
1313  {
1314  merged_datums = lappend(merged_datums, merged_datum);
1315  merged_indexes = lappend_int(merged_indexes, merged_index);
1316  }
1317  }
1318 
1319  /*
1320  * If the NULL partitions (if any) have been proven empty, deem them
1321  * non-existent.
1322  */
1323  if (outer_has_null &&
1324  is_dummy_partition(outer_rel, outer_bi->null_index))
1325  outer_has_null = false;
1326  if (inner_has_null &&
1327  is_dummy_partition(inner_rel, inner_bi->null_index))
1328  inner_has_null = false;
1329 
1330  /* Merge the NULL partitions if any. */
1331  if (outer_has_null || inner_has_null)
1332  merge_null_partitions(&outer_map, &inner_map,
1333  outer_has_null, inner_has_null,
1334  outer_bi->null_index, inner_bi->null_index,
1335  jointype, &next_index, &null_index);
1336  else
1337  Assert(null_index == -1);
1338 
1339  /* Merge the default partitions if any. */
1340  if (outer_has_default || inner_has_default)
1341  merge_default_partitions(&outer_map, &inner_map,
1342  outer_has_default, inner_has_default,
1343  outer_default, inner_default,
1344  jointype, &next_index, &default_index);
1345  else
1346  Assert(default_index == -1);
1347 
1348  /* If we have merged partitions, create the partition bounds. */
1349  if (next_index > 0)
1350  {
1351  /* Fix the merged_indexes list if necessary. */
1352  if (outer_map.did_remapping || inner_map.did_remapping)
1353  {
1354  Assert(jointype == JOIN_FULL);
1355  fix_merged_indexes(&outer_map, &inner_map,
1356  next_index, merged_indexes);
1357  }
1358 
1359  /* Use maps to match partitions from inputs. */
1360  generate_matching_part_pairs(outer_rel, inner_rel,
1361  &outer_map, &inner_map,
1362  next_index,
1363  outer_parts, inner_parts);
1364  Assert(*outer_parts != NIL);
1365  Assert(*inner_parts != NIL);
1366  Assert(list_length(*outer_parts) == list_length(*inner_parts));
1367  Assert(list_length(*outer_parts) <= next_index);
1368 
1369  /* Make a PartitionBoundInfo struct to return. */
1370  merged_bounds = build_merged_partition_bounds(outer_bi->strategy,
1371  merged_datums,
1372  NIL,
1373  merged_indexes,
1374  null_index,
1375  default_index);
1376  Assert(merged_bounds);
1377  }
1378 
1379 cleanup:
1380  /* Free local memory before returning. */
1381  list_free(merged_datums);
1382  list_free(merged_indexes);
1383  free_partition_map(&outer_map);
1384  free_partition_map(&inner_map);
1385 
1386  return merged_bounds;
1387 }
bool did_remapping
Definition: partbounds.c:82
static void generate_matching_part_pairs(RelOptInfo *outer_rel, RelOptInfo *inner_rel, PartitionMap *outer_map, PartitionMap *inner_map, int nmerged, List **outer_parts, List **inner_parts)
Definition: partbounds.c:2341
#define NIL
Definition: pg_list.h:65
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
static PartitionBoundInfo build_merged_partition_bounds(char strategy, List *merged_datums, List *merged_kinds, List *merged_indexes, int null_index, int default_index)
Definition: partbounds.c:2420
#define DatumGetInt32(X)
Definition: postgres.h:472
static int merge_matching_partitions(PartitionMap *outer_map, PartitionMap *inner_map, int outer_part, int inner_part, int *next_index)
Definition: partbounds.c:1764
static int process_inner_partition(PartitionMap *outer_map, PartitionMap *inner_map, bool outer_has_default, bool inner_has_default, int inner_index, int outer_default, JoinType jointype, int *next_index, int *default_index)
Definition: partbounds.c:1964
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:744
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1152
static void free_partition_map(PartitionMap *map)
Definition: partbounds.c:1734
static void fix_merged_indexes(PartitionMap *outer_map, PartitionMap *inner_map, int nmerged, List *merged_indexes)
Definition: partbounds.c:2287
static bool is_dummy_partition(RelOptInfo *rel, int part_index)
Definition: partbounds.c:1745
#define partition_bound_has_default(bi)
Definition: partbounds.h:75
List * lappend_int(List *list, int datum)
Definition: list.c:339
List * lappend(List *list, void *datum)
Definition: list.c:321
uintptr_t Datum
Definition: postgres.h:367
static void cleanup(void)
Definition: bootstrap.c:862
struct PartitionBoundInfoData * boundinfo
Definition: pathnodes.h:745
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:74
static void init_partition_map(RelOptInfo *rel, PartitionMap *map)
Definition: partbounds.c:1713
#define Assert(condition)
Definition: c.h:746
static int list_length(const List *l)
Definition: pg_list.h:149
static void merge_default_partitions(PartitionMap *outer_map, PartitionMap *inner_map, bool outer_has_default, bool inner_has_default, int outer_default, int inner_default, JoinType jointype, int *next_index, int *default_index)
Definition: partbounds.c:2159
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
static int process_outer_partition(PartitionMap *outer_map, PartitionMap *inner_map, bool outer_has_default, bool inner_has_default, int outer_index, int inner_default, JoinType jointype, int *next_index, int *default_index)
Definition: partbounds.c:1882
void list_free(List *list)
Definition: list.c:1376
Definition: pg_list.h:50
static void merge_null_partitions(PartitionMap *outer_map, PartitionMap *inner_map, bool outer_has_null, bool inner_has_null, int outer_null, int inner_null, JoinType jointype, int *next_index, int *null_index)
Definition: partbounds.c:2049

◆ merge_matching_partitions()

static int merge_matching_partitions ( PartitionMap outer_map,
PartitionMap inner_map,
int  outer_part,
int  inner_part,
int *  next_index 
)
static

Definition at line 1764 of file partbounds.c.

References Assert, PartitionMap::did_remapping, PartitionMap::merged, PartitionMap::merged_indexes, and PartitionMap::old_indexes.

Referenced by merge_default_partitions(), merge_list_bounds(), merge_null_partitions(), merge_range_bounds(), process_inner_partition(), and process_outer_partition().

1766 {
1767  int outer_merged_index;
1768  int inner_merged_index;
1769  bool outer_merged;
1770  bool inner_merged;
1771 
1772  Assert(outer_index >= 0 && outer_index < outer_map->nparts);
1773  outer_merged_index = outer_map->merged_indexes[outer_index];
1774  outer_merged = outer_map->merged[outer_index];
1775  Assert(inner_index >= 0 && inner_index < inner_map->nparts);
1776  inner_merged_index = inner_map->merged_indexes[inner_index];
1777  inner_merged = inner_map->merged[inner_index];
1778 
1779  /*
1780  * Handle cases where we have already assigned a merged partition to each
1781  * of the given partitions.
1782  */
1783  if (outer_merged_index >= 0 && inner_merged_index >= 0)
1784  {
1785  /*
1786  * If the mereged partitions are the same, no need to do anything;
1787  * return the index of the merged partitions. Otherwise, if each of
1788  * the given partitions has been merged with a dummy partition on the
1789  * other side, re-map them to either of the two merged partitions.
1790  * Otherwise, they can't be merged, so return -1.
1791  */
1792  if (outer_merged_index == inner_merged_index)
1793  {
1794  Assert(outer_merged);
1795  Assert(inner_merged);
1796  return outer_merged_index;
1797  }
1798  if (!outer_merged && !inner_merged)
1799  {
1800  /*
1801  * This can only happen for a list-partitioning case. We re-map
1802  * them to the merged partition with the smaller of the two merged
1803  * indexes to preserve the property that the canonical order of
1804  * list partitions is determined by the indexes assigned to the
1805  * smallest list value of each partition.
1806  */
1807  if (outer_merged_index < inner_merged_index)
1808  {
1809  outer_map->merged[outer_index] = true;
1810  inner_map->merged_indexes[inner_index] = outer_merged_index;
1811  inner_map->merged[inner_index] = true;
1812  inner_map->did_remapping = true;
1813  inner_map->old_indexes[inner_index] = inner_merged_index;
1814  return outer_merged_index;
1815  }
1816  else
1817  {
1818  inner_map->merged[inner_index] = true;
1819  outer_map->merged_indexes[outer_index] = inner_merged_index;
1820  outer_map->merged[outer_index] = true;
1821  outer_map->did_remapping = true;
1822  outer_map->old_indexes[outer_index] = outer_merged_index;
1823  return inner_merged_index;
1824  }
1825  }
1826  return -1;
1827  }
1828 
1829  /* At least one of the given partitions should not have yet been merged. */
1830  Assert(outer_merged_index == -1 || inner_merged_index == -1);
1831 
1832  /*
1833  * If neither of them has been merged, merge them. Otherwise, if one has
1834  * been merged with a dummy partition on the other side (and the other
1835  * hasn't yet been merged with anything), re-merge them. Otherwise, they
1836  * can't be merged, so return -1.
1837  */
1838  if (outer_merged_index == -1 && inner_merged_index == -1)
1839  {
1840  int merged_index = *next_index;
1841 
1842  Assert(!outer_merged);
1843  Assert(!inner_merged);
1844  outer_map->merged_indexes[outer_index] = merged_index;
1845  outer_map->merged[outer_index] = true;
1846  inner_map->merged_indexes[inner_index] = merged_index;
1847  inner_map->merged[inner_index] = true;
1848  *next_index = *next_index + 1;
1849  return merged_index;
1850  }
1851  if (outer_merged_index >= 0 && !outer_map->merged[outer_index])
1852  {
1853  Assert(inner_merged_index == -1);
1854  Assert(!inner_merged);
1855  inner_map->merged_indexes[inner_index] = outer_merged_index;
1856  inner_map->merged[inner_index] = true;
1857  outer_map->merged[outer_index] = true;
1858  return outer_merged_index;
1859  }
1860  if (inner_merged_index >= 0 && !inner_map->merged[inner_index])
1861  {
1862  Assert(outer_merged_index == -1);
1863  Assert(!outer_merged);
1864  outer_map->merged_indexes[outer_index] = inner_merged_index;
1865  outer_map->merged[outer_index] = true;
1866  inner_map->merged[inner_index] = true;
1867  return inner_merged_index;
1868  }
1869  return -1;
1870 }
bool did_remapping
Definition: partbounds.c:82
bool * merged
Definition: partbounds.c:80
#define Assert(condition)
Definition: c.h:746
int * old_indexes
Definition: partbounds.c:83
int * merged_indexes
Definition: partbounds.c:79

◆ merge_null_partitions()

static void merge_null_partitions ( PartitionMap outer_map,
PartitionMap inner_map,
bool  outer_has_null,
bool  inner_has_null,
int  outer_null,
int  inner_null,
JoinType  jointype,
int *  next_index,
int *  null_index 
)
static

Definition at line 2049 of file partbounds.c.

References Assert, IS_OUTER_JOIN, JOIN_FULL, JOIN_RIGHT, merge_matching_partitions(), merge_partition_with_dummy(), and PartitionMap::merged_indexes.

Referenced by merge_list_bounds().

2058 {
2059  bool consider_outer_null = false;
2060  bool consider_inner_null = false;
2061 
2062  Assert(outer_has_null || inner_has_null);
2063  Assert(*null_index == -1);
2064 
2065  /*
2066  * Check whether the NULL partitions have already been merged and if so,
2067  * set the consider_outer_null/consider_inner_null flags.
2068  */
2069  if (outer_has_null)
2070  {
2071  Assert(outer_null >= 0 && outer_null < outer_map->nparts);
2072  if (outer_map->merged_indexes[outer_null] == -1)
2073  consider_outer_null = true;
2074  }
2075  if (inner_has_null)
2076  {
2077  Assert(inner_null >= 0 && inner_null < inner_map->nparts);
2078  if (inner_map->merged_indexes[inner_null] == -1)
2079  consider_inner_null = true;
2080  }
2081 
2082  /* If both flags are set false, we don't need to do anything. */
2083  if (!consider_outer_null && !consider_inner_null)
2084  return;
2085 
2086  if (consider_outer_null && !consider_inner_null)
2087  {
2088  Assert(outer_has_null);
2089 
2090  /*
2091  * If this is an outer join, the NULL partition on the outer side has
2092  * to be scanned all the way anyway; merge the NULL partition with a
2093  * dummy partition on the other side. In that case
2094  * consider_outer_null means that the NULL partition only contains
2095  * NULL values as the key values, so the merged partition will do so;
2096  * treat it as the NULL partition of the join relation.
2097  */
2098  if (IS_OUTER_JOIN(jointype))
2099  {
2100  Assert(jointype != JOIN_RIGHT);
2101  *null_index = merge_partition_with_dummy(outer_map, outer_null,
2102  next_index);
2103  }
2104  }
2105  else if (!consider_outer_null && consider_inner_null)
2106  {
2107  Assert(inner_has_null);
2108 
2109  /*
2110  * If this is a FULL join, the NULL partition on the inner side has to
2111  * be scanned all the way anyway; merge the NULL partition with a
2112  * dummy partition on the other side. In that case
2113  * consider_inner_null means that the NULL partition only contains
2114  * NULL values as the key values, so the merged partition will do so;
2115  * treat it as the NULL partition of the join relation.
2116  */
2117  if (jointype == JOIN_FULL)
2118  *null_index = merge_partition_with_dummy(inner_map, inner_null,
2119  next_index);
2120  }
2121  else
2122  {
2123  Assert(consider_outer_null && consider_inner_null);
2124  Assert(outer_has_null);
2125  Assert(inner_has_null);
2126 
2127  /*
2128  * If this is an outer join, the NULL partition on the outer side (and
2129  * that on the inner side if this is a FULL join) have to be scanned
2130  * all the way anyway, so merge them. Note that each of the NULL
2131  * partitions isn't merged yet, so they should be merged successfully.
2132  * Like the above, each of the NULL partitions only contains NULL
2133  * values as the key values, so the merged partition will do so; treat
2134  * it as the NULL partition of the join relation.
2135  *
2136  * Note: if this an INNER/SEMI join, the join clause will never be
2137  * satisfied by two NULL values (see comments above), so both the NULL
2138  * partitions can be eliminated.
2139  */
2140  if (IS_OUTER_JOIN(jointype))
2141  {
2142  Assert(jointype != JOIN_RIGHT);
2143  *null_index = merge_matching_partitions(outer_map, inner_map,
2144  outer_null, inner_null,
2145  next_index);
2146  Assert(*null_index >= 0);
2147  }
2148  }
2149 }
static int merge_matching_partitions(PartitionMap *outer_map, PartitionMap *inner_map, int outer_part, int inner_part, int *next_index)
Definition: partbounds.c:1764
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:744
static int merge_partition_with_dummy(PartitionMap *map, int index, int *next_index)
Definition: partbounds.c:2269
#define Assert(condition)
Definition: c.h:746
int * merged_indexes
Definition: partbounds.c:79

◆ merge_partition_with_dummy()

static int merge_partition_with_dummy ( PartitionMap map,
int  index,
int *  next_index 
)
static

Definition at line 2269 of file partbounds.c.

References Assert, PartitionHashBound::index, PartitionMap::merged, and PartitionMap::merged_indexes.

Referenced by merge_default_partitions(), merge_null_partitions(), process_inner_partition(), and process_outer_partition().

2270 {
2271  int merged_index = *next_index;
2272 
2273  Assert(index >= 0 && index < map->nparts);
2274  Assert(map->merged_indexes[index] == -1);
2275  Assert(!map->merged[index]);
2276  map->merged_indexes[index] = merged_index;
2277  /* Leave the merged flag alone! */
2278  *next_index = *next_index + 1;
2279  return merged_index;
2280 }
Definition: type.h:89
bool * merged
Definition: partbounds.c:80
#define Assert(condition)
Definition: c.h:746
int * merged_indexes
Definition: partbounds.c:79

◆ merge_range_bounds()

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 
)
static

Definition at line 1408 of file partbounds.c.

References add_merged_range_bounds(), Assert, RelOptInfo::boundinfo, build_merged_partition_bounds(), cleanup(), compare_range_bounds, compare_range_partitions(), PartitionBoundInfoData::default_index, PartitionMap::did_remapping, free_partition_map(), generate_matching_part_pairs(), get_merged_range_bounds(), get_range_partition(), init_partition_map(), is_dummy_partition(), IS_OUTER_JOIN, JOIN_FULL, list_free(), list_length(), merge_default_partitions(), merge_matching_partitions(), PartitionMap::merged, PartitionMap::merged_indexes, NIL, partition_bound_has_default, PARTITION_STRATEGY_RANGE, process_inner_partition(), process_outer_partition(), and PartitionBoundInfoData::strategy.

Referenced by partition_bounds_merge().

1413 {
1414  PartitionBoundInfo merged_bounds = NULL;
1415  PartitionBoundInfo outer_bi = outer_rel->boundinfo;
1416  PartitionBoundInfo inner_bi = inner_rel->boundinfo;
1417  bool outer_has_default = partition_bound_has_default(outer_bi);
1418  bool inner_has_default = partition_bound_has_default(inner_bi);
1419  int outer_default = outer_bi->default_index;
1420  int inner_default = inner_bi->default_index;
1421  PartitionMap outer_map;
1422  PartitionMap inner_map;
1423  int outer_index;
1424  int inner_index;
1425  int outer_lb_pos;
1426  int inner_lb_pos;
1427  PartitionRangeBound outer_lb;
1428  PartitionRangeBound outer_ub;
1429  PartitionRangeBound inner_lb;
1430  PartitionRangeBound inner_ub;
1431  int next_index = 0;
1432  int default_index = -1;
1433  List *merged_datums = NIL;
1434  List *merged_kinds = NIL;
1435  List *merged_indexes = NIL;
1436 
1437  Assert(*outer_parts == NIL);
1438  Assert(*inner_parts == NIL);
1439  Assert(outer_bi->strategy == inner_bi->strategy &&
1440  outer_bi->strategy == PARTITION_STRATEGY_RANGE);
1441 
1442  init_partition_map(outer_rel, &outer_map);
1443  init_partition_map(inner_rel, &inner_map);
1444 
1445  /*
1446  * If the default partitions (if any) have been proven empty, deem them
1447  * non-existent.
1448  */
1449  if (outer_has_default && is_dummy_partition(outer_rel, outer_default))
1450  outer_has_default = false;
1451  if (inner_has_default && is_dummy_partition(inner_rel, inner_default))
1452  inner_has_default = false;
1453 
1454  /*
1455  * Merge partitions from both sides. In each iteration we compare a pair
1456  * of ranges, one from each side, and decide whether the corresponding
1457  * partitions match or not. If the two ranges overlap, move to the next
1458  * pair of ranges, otherwise move to the next range on the side with a
1459  * lower range. outer_lb_pos/inner_lb_pos keep track of the positions of
1460  * lower bounds in the datums arrays in the outer/inner
1461  * PartitionBoundInfos respectively.
1462  */
1463  outer_lb_pos = inner_lb_pos = 0;
1464  outer_index = get_range_partition(outer_rel, outer_bi, &outer_lb_pos,
1465  &outer_lb, &outer_ub);
1466  inner_index = get_range_partition(inner_rel, inner_bi, &inner_lb_pos,
1467  &inner_lb, &inner_ub);
1468  while (outer_index >= 0 || inner_index >= 0)
1469  {
1470  bool overlap;
1471  int ub_cmpval;
1472  int lb_cmpval;
1473  PartitionRangeBound merged_lb = {-1, NULL, NULL, true};
1474  PartitionRangeBound merged_ub = {-1, NULL, NULL, false};
1475  int merged_index = -1;
1476 
1477  /*
1478  * We run this loop till both sides finish. This allows us to avoid
1479  * duplicating code to handle the remaining ranges on the side which
1480  * finishes later. For that we set the comparison parameter cmpval in
1481  * such a way that it appears as if the side which finishes earlier
1482  * has an extra range higher than any other range on the unfinished
1483  * side. That way we advance the ranges on the unfinished side till
1484  * all of its ranges are exhausted.
1485  */
1486  if (outer_index == -1)
1487  {
1488  overlap = false;
1489  lb_cmpval = 1;
1490  ub_cmpval = 1;
1491  }
1492  else if (inner_index == -1)
1493  {
1494  overlap = false;
1495  lb_cmpval = -1;
1496  ub_cmpval = -1;
1497  }
1498  else
1499  overlap = compare_range_partitions(partnatts, partsupfuncs,
1500  partcollations,
1501  &outer_lb, &outer_ub,
1502  &inner_lb, &inner_ub,
1503  &lb_cmpval, &ub_cmpval);
1504 
1505  if (overlap)
1506  {
1507  /* Two ranges overlap; form a join pair. */
1508 
1509  PartitionRangeBound save_outer_ub;
1510  PartitionRangeBound save_inner_ub;
1511 
1512  /* Both partitions should not have been merged yet. */
1513  Assert(outer_index >= 0);
1514  Assert(outer_map.merged_indexes[outer_index] == -1 &&
1515  outer_map.merged[outer_index] == false);
1516  Assert(inner_index >= 0);
1517  Assert(inner_map.merged_indexes[inner_index] == -1 &&
1518  inner_map.merged[inner_index] == false);
1519 
1520  /*
1521  * Get the index of the merged partition. Both partitions aren't
1522  * merged yet, so the partitions should be merged successfully.
1523  */
1524  merged_index = merge_matching_partitions(&outer_map, &inner_map,
1525  outer_index, inner_index,
1526  &next_index);
1527  Assert(merged_index >= 0);
1528 
1529  /* Get the range bounds of the merged partition. */
1530  get_merged_range_bounds(partnatts, partsupfuncs,
1531  partcollations, jointype,
1532  &outer_lb, &outer_ub,
1533  &inner_lb, &inner_ub,
1534  lb_cmpval, ub_cmpval,
1535  &merged_lb, &merged_ub);
1536 
1537  /* Save the upper bounds of both partitions for use below. */
1538  save_outer_ub = outer_ub;
1539  save_inner_ub = inner_ub;
1540 
1541  /* Move to the next pair of ranges. */
1542  outer_index = get_range_partition(outer_rel, outer_bi, &outer_lb_pos,
1543  &outer_lb, &outer_ub);
1544  inner_index = get_range_partition(inner_rel, inner_bi, &inner_lb_pos,
1545  &inner_lb, &inner_ub);
1546 
1547  /*
1548  * If the range of a partition on one side overlaps the range of
1549  * the next partition on the other side, that will cause the
1550  * partition on one side to match at least two partitions on the
1551  * other side, which is the case that we currently don't support
1552  * partitioned join for; give up.
1553  */
1554  if (ub_cmpval > 0 && inner_index >= 0 &&
1555  compare_range_bounds(partnatts, partsupfuncs, partcollations,
1556  &save_outer_ub, &inner_lb) > 0)
1557  goto cleanup;
1558  if (ub_cmpval < 0 && outer_index >= 0 &&
1559  compare_range_bounds(partnatts, partsupfuncs, partcollations,
1560  &outer_lb, &save_inner_ub) < 0)
1561  goto cleanup;
1562 
1563  /*
1564  * A row from a non-overlapping portion (if any) of a partition on
1565  * one side might find its join partner in the default partition
1566  * (if any) on the other side, causing the same situation as
1567  * above; give up in that case.
1568  */
1569  if ((outer_has_default && (lb_cmpval > 0 || ub_cmpval < 0)) ||
1570  (inner_has_default && (lb_cmpval < 0 || ub_cmpval > 0)))
1571  goto cleanup;
1572  }
1573  else if (ub_cmpval < 0)
1574  {
1575  /* A non-overlapping outer range. */
1576 
1577  /* The outer partition should not have been merged yet. */
1578  Assert(outer_index >= 0);
1579  Assert(outer_map.merged_indexes[outer_index] == -1 &&
1580  outer_map.merged[outer_index] == false);
1581 
1582  /*
1583  * If the inner side has the default partition, or this is an
1584  * outer join, try to assign a merged partition to the outer
1585  * partition (see process_outer_partition()). Otherwise, the
1586  * outer partition will not contribute to the result.
1587  */
1588  if (inner_has_default || IS_OUTER_JOIN(jointype))
1589  {
1590  merged_index = process_outer_partition(&outer_map,
1591  &inner_map,
1592  outer_has_default,
1593  inner_has_default,
1594  outer_index,
1595  inner_default,
1596  jointype,
1597  &next_index,
1598  &default_index);
1599  if (merged_index == -1)
1600  goto cleanup;
1601  merged_lb = outer_lb;
1602  merged_ub = outer_ub;
1603  }
1604 
1605  /* Move to the next range on the outer side. */
1606  outer_index = get_range_partition(outer_rel, outer_bi, &outer_lb_pos,
1607  &outer_lb, &outer_ub);
1608  }
1609  else
1610  {
1611  /* A non-overlapping inner range. */
1612  Assert(ub_cmpval > 0);
1613 
1614  /* The inner partition should not have been merged yet. */
1615  Assert(inner_index >= 0);
1616  Assert(inner_map.merged_indexes[inner_index] == -1 &&
1617  inner_map.merged[inner_index] == false);
1618 
1619  /*
1620  * If the outer side has the default partition, or this is a FULL
1621  * join, try to assign a merged partition to the inner partition
1622  * (see process_inner_partition()). Otherwise, the inner
1623  * partition will not contribute to the result.
1624  */
1625  if (outer_has_default || jointype == JOIN_FULL)
1626  {
1627  merged_index = process_inner_partition(&outer_map,
1628  &inner_map,
1629  outer_has_default,
1630  inner_has_default,
1631  inner_index,
1632  outer_default,
1633  jointype,
1634  &next_index,
1635  &default_index);
1636  if (merged_index == -1)
1637  goto cleanup;
1638  merged_lb = inner_lb;
1639  merged_ub = inner_ub;
1640  }
1641 
1642  /* Move to the next range on the inner side. */
1643  inner_index = get_range_partition(inner_rel, inner_bi, &inner_lb_pos,
1644  &inner_lb, &inner_ub);
1645  }
1646 
1647  /*
1648  * If we assigned a merged partition, add the range bounds and index
1649  * of the merged partition if appropriate.
1650  */
1651  if (merged_index >= 0 && merged_index != default_index)
1652  add_merged_range_bounds(partnatts, partsupfuncs, partcollations,
1653  &merged_lb, &merged_ub, merged_index,
1654  &merged_datums, &merged_kinds,
1655  &merged_indexes);
1656  }
1657 
1658  /* Merge the default partitions if any. */
1659  if (outer_has_default || inner_has_default)
1660  merge_default_partitions(&outer_map, &inner_map,
1661  outer_has_default, inner_has_default,
1662  outer_default, inner_default,
1663  jointype, &next_index, &default_index);
1664  else
1665  Assert(default_index == -1);
1666 
1667  /* If we have merged partitions, create the partition bounds. */
1668  if (next_index > 0)
1669  {
1670  /*
1671  * Unlike the case of list partitioning, we wouldn't have re-merged
1672  * partitions, so did_remapping should be left alone.
1673  */
1674  Assert(!outer_map.did_remapping);
1675  Assert(!inner_map.did_remapping);
1676 
1677  /* Use maps to match partitions from inputs. */
1678  generate_matching_part_pairs(outer_rel, inner_rel,
1679  &outer_map, &inner_map,
1680  next_index,
1681  outer_parts, inner_parts);
1682  Assert(*outer_parts != NIL);
1683  Assert(*inner_parts != NIL);
1684  Assert(list_length(*outer_parts) == list_length(*inner_parts));
1685  Assert(list_length(*outer_parts) == next_index);
1686 
1687  /* Make a PartitionBoundInfo struct to return. */
1688  merged_bounds = build_merged_partition_bounds(outer_bi->strategy,
1689  merged_datums,
1690  merged_kinds,
1691  merged_indexes,
1692  -1,
1693  default_index);
1694  Assert(merged_bounds);
1695  }
1696 
1697 cleanup:
1698  /* Free local memory before returning. */
1699  list_free(merged_datums);
1700  list_free(merged_kinds);
1701  list_free(merged_indexes);
1702  free_partition_map(&outer_map);
1703  free_partition_map(&inner_map);
1704 
1705  return merged_bounds;
1706 }
bool did_remapping
Definition: partbounds.c:82
static void generate_matching_part_pairs(RelOptInfo *outer_rel, RelOptInfo *inner_rel, PartitionMap *outer_map, PartitionMap *inner_map, int nmerged, List **outer_parts, List **inner_parts)
Definition: partbounds.c:2341
#define NIL
Definition: pg_list.h:65
static void get_merged_range_bounds(int partnatts, FmgrInfo *partsupfuncs, Oid *partcollations, JoinType jointype, PartitionRangeBound *outer_lb, PartitionRangeBound *outer_ub, PartitionRangeBound *inner_lb, PartitionRangeBound *inner_ub, int lb_cmpval, int ub_cmpval, PartitionRangeBound *merged_lb, PartitionRangeBound *merged_ub)
Definition: partbounds.c:2609
static PartitionBoundInfo build_merged_partition_bounds(char strategy, List *merged_datums, List *merged_kinds, List *merged_indexes, int null_index, int default_index)
Definition: partbounds.c:2420
static int merge_matching_partitions(PartitionMap *outer_map, PartitionMap *inner_map, int outer_part, int inner_part, int *next_index)
Definition: partbounds.c:1764
static int process_inner_partition(PartitionMap *outer_map, PartitionMap *inner_map, bool outer_has_default, bool inner_has_default, int inner_index, int outer_default, JoinType jointype, int *next_index, int *default_index)
Definition: partbounds.c:1964
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:744
static bool compare_range_partitions(int partnatts, FmgrInfo *partsupfuncs, Oid *partcollations, PartitionRangeBound *outer_lb, PartitionRangeBound *outer_ub, PartitionRangeBound *inner_lb, PartitionRangeBound *inner_ub, int *lb_cmpval, int *ub_cmpval)
Definition: partbounds.c:2560
static void free_partition_map(PartitionMap *map)
Definition: partbounds.c:1734
static void add_merged_range_bounds(int partnatts, FmgrInfo *partsupfuncs, Oid *partcollations, PartitionRangeBound *merged_lb, PartitionRangeBound *merged_ub, int merged_index, List **merged_datums, List **merged_kinds, List **merged_indexes)
Definition: partbounds.c:2673
bool * merged
Definition: partbounds.c:80
static bool is_dummy_partition(RelOptInfo *rel, int part_index)
Definition: partbounds.c:1745
#define partition_bound_has_default(bi)
Definition: partbounds.h:75
static void cleanup(void)
Definition: bootstrap.c:862
struct PartitionBoundInfoData * boundinfo
Definition: pathnodes.h:745
static void init_partition_map(RelOptInfo *rel, PartitionMap *map)
Definition: partbounds.c:1713
#define Assert(condition)
Definition: c.h:746
static int list_length(const List *l)
Definition: pg_list.h:149
static void merge_default_partitions(PartitionMap *outer_map, PartitionMap *inner_map, bool outer_has_default, bool inner_has_default, int outer_default, int inner_default, JoinType jointype, int *next_index, int *default_index)
Definition: partbounds.c:2159
static int process_outer_partition(PartitionMap *outer_map, PartitionMap *inner_map, bool outer_has_default, bool inner_has_default, int outer_index, int inner_default, JoinType jointype, int *next_index, int *default_index)
Definition: partbounds.c:1882
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
void list_free(List *list)
Definition: list.c:1376
#define compare_range_bounds(partnatts, partsupfunc, partcollations, bound1, bound2)
Definition: partbounds.c:88
Definition: pg_list.h:50
int * merged_indexes
Definition: partbounds.c:79
static int get_range_partition(RelOptInfo *rel, PartitionBoundInfo bi, int *lb_pos, PartitionRangeBound *lb, PartitionRangeBound *ub)
Definition: partbounds.c:2479

◆ partition_bounds_copy()

PartitionBoundInfo partition_bounds_copy ( PartitionBoundInfo  src,
PartitionKey  key 
)

Definition at line 917 of file partbounds.c.

References Assert, datumCopy(), PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, generate_unaccent_rules::dest, get_partition_bound_num_indexes(), i, PartitionBoundInfoData::indexes, PartitionBoundInfoData::kind, PartitionBoundInfoData::ndatums, PartitionBoundInfoData::null_index, palloc(), PARTITION_RANGE_DATUM_VALUE, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PartitionKeyData::partnatts, PartitionKeyData::parttypbyval, PartitionKeyData::parttyplen, PartitionKeyData::strategy, and PartitionBoundInfoData::strategy.

Referenced by RelationBuildPartitionDesc().

919 {
921  int i;
922  int ndatums;
923  int partnatts;
924  int num_indexes;
925  bool hash_part;
926  int natts;
927 
929 
930  dest->strategy = src->strategy;
931  ndatums = dest->ndatums = src->ndatums;
932  partnatts = key->partnatts;
933 
934  num_indexes = get_partition_bound_num_indexes(src);
935 
936  /* List partitioned tables have only a single partition key. */
937  Assert(key->strategy != PARTITION_STRATEGY_LIST || partnatts == 1);
938 
939  dest->datums = (Datum **) palloc(sizeof(Datum *) * ndatums);
940 
941  if (src->kind != NULL)
942  {
943  dest->kind = (PartitionRangeDatumKind **) palloc(ndatums *
944  sizeof(PartitionRangeDatumKind *));
945  for (i = 0; i < ndatums; i++)
946  {
947  dest->kind[i] = (PartitionRangeDatumKind *) palloc(partnatts *
948  sizeof(PartitionRangeDatumKind));
949 
950  memcpy(dest->kind[i], src->kind[i],
951  sizeof(PartitionRangeDatumKind) * key->partnatts);
952  }
953  }
954  else
955  dest->kind = NULL;
956 
957  /*
958  * For hash partitioning, datums array will have two elements - modulus
959  * and remainder.
960  */
961  hash_part = (key->strategy == PARTITION_STRATEGY_HASH);
962  natts = hash_part ? 2 : partnatts;
963 
964  for (i = 0; i < ndatums; i++)
965  {
966  int j;
967 
968  dest->datums[i] = (Datum *) palloc(sizeof(Datum) * natts);
969 
970  for (j = 0; j < natts; j++)
971  {
972  bool byval;
973  int typlen;
974 
975  if (hash_part)
976  {
977  typlen = sizeof(int32); /* Always int4 */
978  byval = true; /* int4 is pass-by-value */
979  }
980  else
981  {
982  byval = key->parttypbyval[j];
983  typlen = key->parttyplen[j];
984  }
985 
986  if (dest->kind == NULL ||
987  dest->kind[i][j] == PARTITION_RANGE_DATUM_VALUE)
988  dest->datums[i][j] = datumCopy(src->datums[i][j],
989  byval, typlen);
990  }
991  }
992 
993  dest->indexes = (int *) palloc(sizeof(int) * num_indexes);
994  memcpy(dest->indexes, src->indexes, sizeof(int) * num_indexes);
995 
996  dest->null_index = src->null_index;
997  dest->default_index = src->default_index;
998 
999  return dest;
1000 }
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
PartitionRangeDatumKind
Definition: parsenodes.h:837
signed int int32
Definition: c.h:363
static int get_partition_bound_num_indexes(PartitionBoundInfo b)
Definition: partbounds.c:3706
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:131
uintptr_t Datum
Definition: postgres.h:367
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
bool * parttypbyval
Definition: partcache.h:44
#define Assert(condition)
Definition: c.h:746
int16 * parttyplen
Definition: partcache.h:43
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
void * palloc(Size size)
Definition: mcxt.c:950
int i
struct PartitionBoundInfoData * PartitionBoundInfo
Definition: partdefs.h:16

◆ partition_bounds_create()

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

Definition at line 305 of file partbounds.c.

References Assert, create_hash_bounds(), create_list_bounds(), create_range_bounds(), elog, ERROR, i, palloc(), PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, and PartitionKeyData::strategy.

Referenced by RelationBuildPartitionDesc().

307 {
308  int i;
309 
310  Assert(nparts > 0);
311 
312  /*
313  * For each partitioning method, we first convert the partition bounds
314  * from their parser node representation to the internal representation,
315  * along with any additional preprocessing (such as de-duplicating range
316  * bounds). Resulting bound datums are then added to the 'datums' array
317  * in PartitionBoundInfo. For each datum added, an integer indicating the
318  * canonical partition index is added to the 'indexes' array.
319  *
320  * For each bound, we remember its partition's position (0-based) in the
321  * original list to later map it to the canonical index.
322  */
323 
324  /*
325  * Initialize mapping array with invalid values, this is filled within
326  * each sub-routine below depending on the bound type.
327  */
328  *mapping = (int *) palloc(sizeof(int) * nparts);
329  for (i = 0; i < nparts; i++)
330  (*mapping)[i] = -1;
331 
332  switch (key->strategy)
333  {
335  return create_hash_bounds(boundspecs, nparts, key, mapping);
336 
338  return create_list_bounds(boundspecs, nparts, key, mapping);
339 
341  return create_range_bounds(boundspecs, nparts, key, mapping);
342 
343  default:
344  elog(ERROR, "unexpected partition strategy: %d",
345  (int) key->strategy);
346  break;
347  }
348 
349  Assert(false);
350  return NULL; /* keep compiler quiet */
351 }
#define ERROR
Definition: elog.h:43
static PartitionBoundInfo create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
Definition: partbounds.c:358
static PartitionBoundInfo create_list_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
Definition: partbounds.c:440
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
#define Assert(condition)
Definition: c.h:746
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
static PartitionBoundInfo create_range_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
Definition: partbounds.c:597
void * palloc(Size size)
Definition: mcxt.c:950
#define elog(elevel,...)
Definition: elog.h:214
int i

◆ partition_bounds_equal()

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

Definition at line 799 of file partbounds.c.

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

Referenced by compute_partition_bounds().

801 {
802  int i;
803 
804  if (b1->strategy != b2->strategy)
805  return false;
806 
807  if (b1->ndatums != b2->ndatums)
808  return false;
809 
810  if (b1->null_index != b2->null_index)
811  return false;
812 
813  if (b1->default_index != b2->default_index)
814  return false;
815 
817  {
818  int greatest_modulus = get_hash_partition_greatest_modulus(b1);
819 
820  /*
821  * If two hash partitioned tables have different greatest moduli,
822  * their partition schemes don't match.
823  */
824  if (greatest_modulus != get_hash_partition_greatest_modulus(b2))
825  return false;
826 
827  /*
828  * We arrange the partitions in the ascending order of their moduli
829  * and remainders. Also every modulus is factor of next larger
830  * modulus. Therefore we can safely store index of a given partition
831  * in indexes array at remainder of that partition. Also entries at
832  * (remainder + N * modulus) positions in indexes array are all same
833  * for (modulus, remainder) specification for any partition. Thus
834  * datums array from both the given bounds are same, if and only if
835  * their indexes array will be same. So, it suffices to compare
836  * indexes array.
837  */
838  for (i = 0; i < greatest_modulus; i++)
839  if (b1->indexes[i] != b2->indexes[i])
840  return false;
841 
842 #ifdef USE_ASSERT_CHECKING
843 
844  /*
845  * Nonetheless make sure that the bounds are indeed same when the
846  * indexes match. Hash partition bound stores modulus and remainder
847  * at b1->datums[i][0] and b1->datums[i][1] position respectively.
848  */
849  for (i = 0; i < b1->ndatums; i++)
850  Assert((b1->datums[i][0] == b2->datums[i][0] &&
851  b1->datums[i][1] == b2->datums[i][1]));
852 #endif
853  }
854  else
855  {
856  for (i = 0; i < b1->ndatums; i++)
857  {
858  int j;
859 
860  for (j = 0; j < partnatts; j++)
861  {
862  /* For range partitions, the bounds might not be finite. */
863  if (b1->kind != NULL)
864  {
865  /* The different kinds of bound all differ from each other */
866  if (b1->kind[i][j] != b2->kind[i][j])
867  return false;
868 
869  /*
870  * Non-finite bounds are equal without further
871  * examination.
872  */
873  if (b1->kind[i][j] != PARTITION_RANGE_DATUM_VALUE)
874  continue;
875  }
876 
877  /*
878  * Compare the actual values. Note that it would be both
879  * incorrect and unsafe to invoke the comparison operator
880  * derived from the partitioning specification here. It would
881  * be incorrect because we want the relcache entry to be
882  * updated for ANY change to the partition bounds, not just
883  * those that the partitioning operator thinks are
884  * significant. It would be unsafe because we might reach
885  * this code in the context of an aborted transaction, and an
886  * arbitrary partitioning operator might not be safe in that
887  * context. datumIsEqual() should be simple enough to be
888  * safe.
889  */
890  if (!datumIsEqual(b1->datums[i][j], b2->datums[i][j],
891  parttypbyval[j], parttyplen[j]))
892  return false;
893  }
894 
895  if (b1->indexes[i] != b2->indexes[i])
896  return false;
897  }
898 
899  /* There are ndatums+1 indexes in case of range partitions */
900  if (b1->strategy == PARTITION_STRATEGY_RANGE &&
901  b1->indexes[i] != b2->indexes[i])
902  return false;
903  }
904  return true;
905 }
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
bool datumIsEqual(Datum value1, Datum value2, bool typByVal, int typLen)
Definition: datum.c:222
int get_hash_partition_greatest_modulus(PartitionBoundInfo bound)
Definition: partbounds.c:3291
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801