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 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:46
static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partbounds.c:3383

Definition at line 88 of file partbounds.c.

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

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

2671 {
2672  int cmpval;
2673 
2674  if (!*merged_datums)
2675  {
2676  /* First merged partition */
2677  Assert(!*merged_kinds);
2678  Assert(!*merged_indexes);
2679  cmpval = 1;
2680  }
2681  else
2682  {
2683  PartitionRangeBound prev_ub;
2684 
2685  Assert(*merged_datums);
2686  Assert(*merged_kinds);
2687  Assert(*merged_indexes);
2688 
2689  /* Get the last upper bound. */
2690  prev_ub.index = llast_int(*merged_indexes);
2691  prev_ub.datums = (Datum *) llast(*merged_datums);
2692  prev_ub.kind = (PartitionRangeDatumKind *) llast(*merged_kinds);
2693  prev_ub.lower = false;
2694 
2695  /*
2696  * We pass lower1 = false to partition_rbound_cmp() to prevent it from
2697  * considering the last upper bound to be smaller than the lower bound
2698  * of the merged partition when the values of the two range bounds
2699  * compare equal.
2700  */
2701  cmpval = partition_rbound_cmp(partnatts, partsupfuncs, partcollations,
2702  merged_lb->datums, merged_lb->kind,
2703  false, &prev_ub);
2704  Assert(cmpval >= 0);
2705  }
2706 
2707  /*
2708  * If the lower bound is higher than the last upper bound, add the lower
2709  * bound with the index as -1 indicating that that is a lower bound; else,
2710  * the last upper bound will be reused as the lower bound of the merged
2711  * partition, so skip this.
2712  */
2713  if (cmpval > 0)
2714  {
2715  *merged_datums = lappend(*merged_datums, merged_lb->datums);
2716  *merged_kinds = lappend(*merged_kinds, merged_lb->kind);
2717  *merged_indexes = lappend_int(*merged_indexes, -1);
2718  }
2719 
2720  /* Add the upper bound and index of the merged partition. */
2721  *merged_datums = lappend(*merged_datums, merged_ub->datums);
2722  *merged_kinds = lappend(*merged_kinds, merged_ub->kind);
2723  *merged_indexes = lappend_int(*merged_indexes, merged_index);
2724 }
PartitionRangeDatumKind * kind
Definition: partbounds.c:68
PartitionRangeDatumKind
Definition: parsenodes.h:850
#define llast(l)
Definition: pg_list.h:194
List * lappend_int(List *list, int datum)
Definition: list.c:354
List * lappend(List *list, void *datum)
Definition: list.c:336
uintptr_t Datum
Definition: postgres.h:411
static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partbounds.c:3383
#define Assert(condition)
Definition: c.h:804
#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 2409 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::nindexes, PartitionBoundInfoData::null_index, palloc(), PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, and PartitionBoundInfoData::strategy.

Referenced by merge_list_bounds(), and merge_range_bounds().

2412 {
2413  PartitionBoundInfo merged_bounds;
2414  int ndatums = list_length(merged_datums);
2415  int pos;
2416  ListCell *lc;
2417 
2418  merged_bounds = (PartitionBoundInfo) palloc(sizeof(PartitionBoundInfoData));
2419  merged_bounds->strategy = strategy;
2420  merged_bounds->ndatums = ndatums;
2421 
2422  merged_bounds->datums = (Datum **) palloc(sizeof(Datum *) * ndatums);
2423  pos = 0;
2424  foreach(lc, merged_datums)
2425  merged_bounds->datums[pos++] = (Datum *) lfirst(lc);
2426 
2427  if (strategy == PARTITION_STRATEGY_RANGE)
2428  {
2429  Assert(list_length(merged_kinds) == ndatums);
2430  merged_bounds->kind = (PartitionRangeDatumKind **)
2431  palloc(sizeof(PartitionRangeDatumKind *) * ndatums);
2432  pos = 0;
2433  foreach(lc, merged_kinds)
2434  merged_bounds->kind[pos++] = (PartitionRangeDatumKind *) lfirst(lc);
2435 
2436  /* There are ndatums+1 indexes in the case of range partitioning. */
2437  merged_indexes = lappend_int(merged_indexes, -1);
2438  ndatums++;
2439  }
2440  else
2441  {
2442  Assert(strategy == PARTITION_STRATEGY_LIST);
2443  Assert(merged_kinds == NIL);
2444  merged_bounds->kind = NULL;
2445  }
2446 
2447  Assert(list_length(merged_indexes) == ndatums);
2448  merged_bounds->nindexes = ndatums;
2449  merged_bounds->indexes = (int *) palloc(sizeof(int) * ndatums);
2450  pos = 0;
2451  foreach(lc, merged_indexes)
2452  merged_bounds->indexes[pos++] = lfirst_int(lc);
2453 
2454  merged_bounds->null_index = null_index;
2455  merged_bounds->default_index = default_index;
2456 
2457  return merged_bounds;
2458 }
#define NIL
Definition: pg_list.h:65
PartitionRangeDatumKind ** kind
Definition: partbounds.h:70
PartitionRangeDatumKind
Definition: parsenodes.h:850
#define lfirst_int(lc)
Definition: pg_list.h:170
List * lappend_int(List *list, int datum)
Definition: list.c:354
uintptr_t Datum
Definition: postgres.h:411
#define Assert(condition)
Definition: c.h:804
#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:815
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:816
void * palloc(Size size)
Definition: mcxt.c:1062
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 3146 of file partbounds.c.

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

Referenced by DefineRelation().

3148 {
3149  List *new_part_constraints;
3150  List *def_part_constraints;
3151  List *all_parts;
3152  ListCell *lc;
3153 
3154  new_part_constraints = (new_spec->strategy == PARTITION_STRATEGY_LIST)
3155  ? get_qual_for_list(parent, new_spec)
3156  : get_qual_for_range(parent, new_spec, false);
3157  def_part_constraints =
3158  get_proposed_default_constraint(new_part_constraints);
3159 
3160  /*
3161  * Map the Vars in the constraint expression from parent's attnos to
3162  * default_rel's.
3163  */
3164  def_part_constraints =
3165  map_partition_varattnos(def_part_constraints, 1, default_rel,
3166  parent);
3167 
3168  /*
3169  * If the existing constraints on the default partition imply that it will
3170  * not contain any row that would belong to the new partition, we can
3171  * avoid scanning the default partition.
3172  */
3173  if (PartConstraintImpliedByRelConstraint(default_rel, def_part_constraints))
3174  {
3175  ereport(DEBUG1,
3176  (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
3177  RelationGetRelationName(default_rel))));
3178  return;
3179  }
3180 
3181  /*
3182  * Scan the default partition and its subpartitions, and check for rows
3183  * that do not satisfy the revised partition constraints.
3184  */
3185  if (default_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
3186  all_parts = find_all_inheritors(RelationGetRelid(default_rel),
3187  AccessExclusiveLock, NULL);
3188  else
3189  all_parts = list_make1_oid(RelationGetRelid(default_rel));
3190 
3191  foreach(lc, all_parts)
3192  {
3193  Oid part_relid = lfirst_oid(lc);
3194  Relation part_rel;
3195  Expr *partition_constraint;
3196  EState *estate;
3197  ExprState *partqualstate = NULL;
3198  Snapshot snapshot;
3199  ExprContext *econtext;
3200  TableScanDesc scan;
3201  MemoryContext oldCxt;
3202  TupleTableSlot *tupslot;
3203 
3204  /* Lock already taken above. */
3205  if (part_relid != RelationGetRelid(default_rel))
3206  {
3207  part_rel = table_open(part_relid, NoLock);
3208 
3209  /*
3210  * Map the Vars in the constraint expression from default_rel's
3211  * the sub-partition's.
3212  */
3213  partition_constraint = make_ands_explicit(def_part_constraints);
3214  partition_constraint = (Expr *)
3215  map_partition_varattnos((List *) partition_constraint, 1,
3216  part_rel, default_rel);
3217 
3218  /*
3219  * If the partition constraints on default partition child imply
3220  * that it will not contain any row that would belong to the new
3221  * partition, we can avoid scanning the child table.
3222  */
3224  def_part_constraints))
3225  {
3226  ereport(DEBUG1,
3227  (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
3228  RelationGetRelationName(part_rel))));
3229 
3230  table_close(part_rel, NoLock);
3231  continue;
3232  }
3233  }
3234  else
3235  {
3236  part_rel = default_rel;
3237  partition_constraint = make_ands_explicit(def_part_constraints);
3238  }
3239 
3240  /*
3241  * Only RELKIND_RELATION relations (i.e. leaf partitions) need to be
3242  * scanned.
3243  */
3244  if (part_rel->rd_rel->relkind != RELKIND_RELATION)
3245  {
3246  if (part_rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
3247  ereport(WARNING,
3248  (errcode(ERRCODE_CHECK_VIOLATION),
3249  errmsg("skipped scanning foreign table \"%s\" which is a partition of default partition \"%s\"",
3250  RelationGetRelationName(part_rel),
3251  RelationGetRelationName(default_rel))));
3252 
3253  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
3254  table_close(part_rel, NoLock);
3255 
3256  continue;
3257  }
3258 
3259  estate = CreateExecutorState();
3260 
3261  /* Build expression execution states for partition check quals */
3262  partqualstate = ExecPrepareExpr(partition_constraint, estate);
3263 
3264  econtext = GetPerTupleExprContext(estate);
3265  snapshot = RegisterSnapshot(GetLatestSnapshot());
3266  tupslot = table_slot_create(part_rel, &estate->es_tupleTable);
3267  scan = table_beginscan(part_rel, snapshot, 0, NULL);
3268 
3269  /*
3270  * Switch to per-tuple memory context and reset it for each tuple
3271  * produced, so we don't leak memory.
3272  */
3274 
3275  while (table_scan_getnextslot(scan, ForwardScanDirection, tupslot))
3276  {
3277  econtext->ecxt_scantuple = tupslot;
3278 
3279  if (!ExecCheck(partqualstate, econtext))
3280  ereport(ERROR,
3281  (errcode(ERRCODE_CHECK_VIOLATION),
3282  errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
3283  RelationGetRelationName(default_rel)),
3284  errtable(default_rel)));
3285 
3286  ResetExprContext(econtext);
3288  }
3289 
3290  MemoryContextSwitchTo(oldCxt);
3291  table_endscan(scan);
3292  UnregisterSnapshot(snapshot);
3294  FreeExecutorState(estate);
3295 
3296  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
3297  table_close(part_rel, NoLock); /* keep the lock until commit */
3298  }
3299 }
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:698
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:1032
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:746
void FreeExecutorState(EState *estate)
Definition: execUtils.c:186
#define GetPerTupleExprContext(estate)
Definition: executor.h:533
#define ERROR
Definition: elog.h:46
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, struct ScanKeyData *key)
Definition: tableam.h:883
#define NoLock
Definition: lockdefs.h:34
List * get_proposed_default_constraint(List *new_part_constraints)
Definition: partition.c:368
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1254
#define RelationGetRelationName(relation)
Definition: rel.h:503
EState * CreateExecutorState(void)
Definition: execUtils.c:90
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:852
#define WARNING
Definition: elog.h:40
List * es_tupleTable
Definition: execnodes.h:601
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:708
#define list_make1_oid(x1)
Definition: pg_list.h:236
#define ereport(elevel,...)
Definition: elog.h:157
int errmsg_internal(const char *fmt,...)
Definition: elog.c:996
static List * get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default)
Definition: partbounds.c:4169
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:226
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:815
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:325
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:538
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:991
#define AccessExclusiveLock
Definition: lockdefs.h:45
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:256
int errmsg(const char *fmt,...)
Definition: elog.c:909
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition: execExpr.c:853
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:120
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
Definition: pg_list.h:50
int errtable(Relation rel)
Definition: relcache.c:5628
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition: partition.c:221
#define RelationGetRelid(relation)
Definition: rel.h:469
static List * get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:3960
#define ResetExprContext(econtext)
Definition: executor.h:527
#define lfirst_oid(lc)
Definition: pg_list.h:171
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:16890

◆ check_new_partition_bound()

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

Definition at line 2797 of file partbounds.c.

References Abs, Assert, PartitionDescData::boundinfo, castNode, Const::constisnull, Const::constvalue, DatumGetInt32, PartitionRangeBound::datums, PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, elog, equal(), ereport, errcode(), errdetail(), errmsg(), ERROR, get_range_partbound_string(), get_rel_name(), PartitionBoundInfoData::indexes, PartitionBoundSpec::is_default, sort-test::key, PartitionRangeBound::kind, PartitionBoundInfoData::kind, lfirst, linitial, list_nth(), PartitionBoundSpec::listdatums, Const::location, PartitionBoundSpec::location, PartitionRangeDatum::location, lower(), PartitionBoundSpec::lowerdatums, make_one_partition_rbound(), PartitionBoundSpec::modulus, PartitionBoundInfoData::ndatums, PartitionBoundInfoData::nindexes, PartitionDescData::nparts, PartitionBoundInfoData::null_index, PartitionDescData::oids, parser_errposition(), 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().

2799 {
2801  PartitionDesc partdesc = RelationGetPartitionDesc(parent, false);
2802  PartitionBoundInfo boundinfo = partdesc->boundinfo;
2803  int with = -1;
2804  bool overlap = false;
2805  int overlap_location = -1;
2806 
2807  if (spec->is_default)
2808  {
2809  /*
2810  * The default partition bound never conflicts with any other
2811  * partition's; if that's what we're attaching, the only possible
2812  * problem is that one already exists, so check for that and we're
2813  * done.
2814  */
2815  if (boundinfo == NULL || !partition_bound_has_default(boundinfo))
2816  return;
2817 
2818  /* Default partition already exists, error out. */
2819  ereport(ERROR,
2820  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2821  errmsg("partition \"%s\" conflicts with existing default partition \"%s\"",
2822  relname, get_rel_name(partdesc->oids[boundinfo->default_index])),
2823  parser_errposition(pstate, spec->location)));
2824  }
2825 
2826  switch (key->strategy)
2827  {
2829  {
2831  Assert(spec->remainder >= 0 && spec->remainder < spec->modulus);
2832 
2833  if (partdesc->nparts > 0)
2834  {
2835  int greatest_modulus;
2836  int remainder;
2837  int offset;
2838 
2839  /*
2840  * Check rule that every modulus must be a factor of the
2841  * next larger modulus. For example, if you have a bunch
2842  * of partitions that all have modulus 5, you can add a
2843  * new partition with modulus 10 or a new partition with
2844  * modulus 15, but you cannot add both a partition with
2845  * modulus 10 and a partition with modulus 15, because 10
2846  * is not a factor of 15.
2847  */
2848 
2849  /*
2850  * Get the greatest (modulus, remainder) pair contained in
2851  * boundinfo->datums that is less than or equal to the
2852  * (spec->modulus, spec->remainder) pair.
2853  */
2854  offset = partition_hash_bsearch(boundinfo,
2855  spec->modulus,
2856  spec->remainder);
2857  if (offset < 0)
2858  {
2859  int next_modulus;
2860 
2861  /*
2862  * All existing moduli are greater or equal, so the
2863  * new one must be a factor of the smallest one, which
2864  * is first in the boundinfo.
2865  */
2866  next_modulus = DatumGetInt32(boundinfo->datums[0][0]);
2867  if (next_modulus % spec->modulus != 0)
2868  ereport(ERROR,
2869  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2870  errmsg("every hash partition modulus must be a factor of the next larger modulus"),
2871  errdetail("The new modulus %d is not a factor of %d, the modulus of existing partition \"%s\".",
2872  spec->modulus, next_modulus,
2873  get_rel_name(partdesc->oids[boundinfo->indexes[0]]))));
2874  }
2875  else
2876  {
2877  int prev_modulus;
2878 
2879  /*
2880  * We found the largest modulus less than or equal to
2881  * ours.
2882  */
2883  prev_modulus = DatumGetInt32(boundinfo->datums[offset][0]);
2884 
2885  if (spec->modulus % prev_modulus != 0)
2886  ereport(ERROR,
2887  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2888  errmsg("every hash partition modulus must be a factor of the next larger modulus"),
2889  errdetail("The new modulus %d is not divisible by %d, the modulus of existing partition \"%s\".",
2890  spec->modulus,
2891  prev_modulus,
2892  get_rel_name(partdesc->oids[boundinfo->indexes[offset]]))));
2893 
2894  if (offset + 1 < boundinfo->ndatums)
2895  {
2896  int next_modulus;
2897 
2898  /* Look at the next higher modulus */
2899  next_modulus = DatumGetInt32(boundinfo->datums[offset + 1][0]);
2900 
2901  if (next_modulus % spec->modulus != 0)
2902  ereport(ERROR,
2903  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2904  errmsg("every hash partition modulus must be a factor of the next larger modulus"),
2905  errdetail("The new modulus %d is not a factor of %d, the modulus of existing partition \"%s\".",
2906  spec->modulus, next_modulus,
2907  get_rel_name(partdesc->oids[boundinfo->indexes[offset + 1]]))));
2908  }
2909  }
2910 
2911  greatest_modulus = boundinfo->nindexes;
2912  remainder = spec->remainder;
2913 
2914  /*
2915  * Normally, the lowest remainder that could conflict with
2916  * the new partition is equal to the remainder specified
2917  * for the new partition, but when the new partition has a
2918  * modulus higher than any used so far, we need to adjust.
2919  */
2920  if (remainder >= greatest_modulus)
2921  remainder = remainder % greatest_modulus;
2922 
2923  /* Check every potentially-conflicting remainder. */
2924  do
2925  {
2926  if (boundinfo->indexes[remainder] != -1)
2927  {
2928  overlap = true;
2929  overlap_location = spec->location;
2930  with = boundinfo->indexes[remainder];
2931  break;
2932  }
2933  remainder += spec->modulus;
2934  } while (remainder < greatest_modulus);
2935  }
2936 
2937  break;
2938  }
2939 
2941  {
2943 
2944  if (partdesc->nparts > 0)
2945  {
2946  ListCell *cell;
2947 
2948  Assert(boundinfo &&
2949  boundinfo->strategy == PARTITION_STRATEGY_LIST &&
2950  (boundinfo->ndatums > 0 ||
2951  partition_bound_accepts_nulls(boundinfo) ||
2952  partition_bound_has_default(boundinfo)));
2953 
2954  foreach(cell, spec->listdatums)
2955  {
2956  Const *val = castNode(Const, lfirst(cell));
2957 
2958  overlap_location = val->location;
2959  if (!val->constisnull)
2960  {
2961  int offset;
2962  bool equal;
2963 
2964  offset = partition_list_bsearch(&key->partsupfunc[0],
2965  key->partcollation,
2966  boundinfo,
2967  val->constvalue,
2968  &equal);
2969  if (offset >= 0 && equal)
2970  {
2971  overlap = true;
2972  with = boundinfo->indexes[offset];
2973  break;
2974  }
2975  }
2976  else if (partition_bound_accepts_nulls(boundinfo))
2977  {
2978  overlap = true;
2979  with = boundinfo->null_index;
2980  break;
2981  }
2982  }
2983  }
2984 
2985  break;
2986  }
2987 
2989  {
2991  *upper;
2992  int cmpval;
2993 
2995  lower = make_one_partition_rbound(key, -1, spec->lowerdatums, true);
2996  upper = make_one_partition_rbound(key, -1, spec->upperdatums, false);
2997 
2998  /*
2999  * First check if the resulting range would be empty with
3000  * specified lower and upper bounds. partition_rbound_cmp
3001  * cannot return zero here, since the lower-bound flags are
3002  * different.
3003  */
3004  cmpval = partition_rbound_cmp(key->partnatts,
3005  key->partsupfunc,
3006  key->partcollation,
3007  lower->datums, lower->kind,
3008  true, upper);
3009  Assert(cmpval != 0);
3010  if (cmpval > 0)
3011  {
3012  /* Point to problematic key in the lower datums list. */
3013  PartitionRangeDatum *datum = list_nth(spec->lowerdatums,
3014  cmpval - 1);
3015 
3016  ereport(ERROR,
3017  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3018  errmsg("empty range bound specified for partition \"%s\"",
3019  relname),
3020  errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
3023  parser_errposition(pstate, datum->location)));
3024  }
3025 
3026  if (partdesc->nparts > 0)
3027  {
3028  int offset;
3029 
3030  Assert(boundinfo &&
3031  boundinfo->strategy == PARTITION_STRATEGY_RANGE &&
3032  (boundinfo->ndatums > 0 ||
3033  partition_bound_has_default(boundinfo)));
3034 
3035  /*
3036  * Test whether the new lower bound (which is treated
3037  * inclusively as part of the new partition) lies inside
3038  * an existing partition, or in a gap.
3039  *
3040  * If it's inside an existing partition, the bound at
3041  * offset + 1 will be the upper bound of that partition,
3042  * and its index will be >= 0.
3043  *
3044  * If it's in a gap, the bound at offset + 1 will be the
3045  * lower bound of the next partition, and its index will
3046  * be -1. This is also true if there is no next partition,
3047  * since the index array is initialised with an extra -1
3048  * at the end.
3049  */
3050  offset = partition_range_bsearch(key->partnatts,
3051  key->partsupfunc,
3052  key->partcollation,
3053  boundinfo, lower,
3054  &cmpval);
3055 
3056  if (boundinfo->indexes[offset + 1] < 0)
3057  {
3058  /*
3059  * Check that the new partition will fit in the gap.
3060  * For it to fit, the new upper bound must be less
3061  * than or equal to the lower bound of the next
3062  * partition, if there is one.
3063  */
3064  if (offset + 1 < boundinfo->ndatums)
3065  {
3066  Datum *datums;
3068  bool is_lower;
3069 
3070  datums = boundinfo->datums[offset + 1];
3071  kind = boundinfo->kind[offset + 1];
3072  is_lower = (boundinfo->indexes[offset + 1] == -1);
3073 
3074  cmpval = partition_rbound_cmp(key->partnatts,
3075  key->partsupfunc,
3076  key->partcollation,
3077  datums, kind,
3078  is_lower, upper);
3079  if (cmpval < 0)
3080  {
3081  /*
3082  * Point to problematic key in the upper
3083  * datums list.
3084  */
3085  PartitionRangeDatum *datum =
3086  list_nth(spec->upperdatums, Abs(cmpval) - 1);
3087 
3088  /*
3089  * The new partition overlaps with the
3090  * existing partition between offset + 1 and
3091  * offset + 2.
3092  */
3093  overlap = true;
3094  overlap_location = datum->location;
3095  with = boundinfo->indexes[offset + 2];
3096  }
3097  }
3098  }
3099  else
3100  {
3101  /*
3102  * The new partition overlaps with the existing
3103  * partition between offset and offset + 1.
3104  */
3105  PartitionRangeDatum *datum;
3106 
3107  /*
3108  * Point to problematic key in the lower datums list;
3109  * if we have equality, point to the first one.
3110  */
3111  datum = cmpval == 0 ? linitial(spec->lowerdatums) :
3112  list_nth(spec->lowerdatums, Abs(cmpval) - 1);
3113  overlap = true;
3114  overlap_location = datum->location;
3115  with = boundinfo->indexes[offset + 1];
3116  }
3117  }
3118 
3119  break;
3120  }
3121 
3122  default:
3123  elog(ERROR, "unexpected partition strategy: %d",
3124  (int) key->strategy);
3125  }
3126 
3127  if (overlap)
3128  {
3129  Assert(with >= 0);
3130  ereport(ERROR,
3131  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3132  errmsg("partition \"%s\" would overlap partition \"%s\"",
3133  relname, get_rel_name(partdesc->oids[with])),
3134  parser_errposition(pstate, overlap_location)));
3135  }
3136 }
Datum constvalue
Definition: primnodes.h:219
PartitionRangeDatumKind ** kind
Definition: partbounds.h:70
PartitionRangeDatumKind * kind
Definition: partbounds.c:68
#define DatumGetInt32(X)
Definition: postgres.h:516
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3113
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:46
FmgrInfo * partsupfunc
Definition: partcache.h:35
#define castNode(_type_, nodeptr)
Definition: nodes.h:608
PartitionRangeDatumKind
Definition: parsenodes.h:850
int errcode(int sqlerrcode)
Definition: elog.c:698
Datum upper(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:77
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
NameData relname
Definition: pg_class.h:38
PartitionBoundInfo boundinfo
Definition: partdesc.h:38
#define Abs(x)
Definition: c.h:992
static PartitionRangeBound * make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
Definition: partbounds.c:3323
#define linitial(l)
Definition: pg_list.h:174
#define ERROR
Definition: elog.h:46
static void * list_nth(const List *list, int n)
Definition: pg_list.h:278
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition: partdesc.c:72
int errdetail(const char *fmt,...)
Definition: elog.c:1042
#define partition_bound_has_default(bi)
Definition: partbounds.h:82
int partition_hash_bsearch(PartitionBoundInfo boundinfo, int modulus, int remainder)
Definition: partbounds.c:3633
char * get_range_partbound_string(List *bound_datums)
Definition: ruleutils.c:11970
Oid * partcollation
Definition: partcache.h:38
int location
Definition: primnodes.h:226
uintptr_t Datum
Definition: postgres.h:411
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:814
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:81
int partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
Definition: partbounds.c:3502
#define ereport(elevel,...)
Definition: elog.h:157
static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partbounds.c:3383
#define Assert(condition)
Definition: c.h:804
#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:3548
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:815
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:816
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define elog(elevel,...)
Definition: elog.h:232
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1899
long val
Definition: informix.c:664
bool constisnull
Definition: primnodes.h:220

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

References compare_range_bounds.

Referenced by merge_range_bounds().

2557 {
2558  /*
2559  * Check if the outer partition's upper bound is lower than the inner
2560  * partition's lower bound; if so the partitions aren't overlapping.
2561  */
2562  if (compare_range_bounds(partnatts, partsupfuncs, partcollations,
2563  outer_ub, inner_lb) < 0)
2564  {
2565  *lb_cmpval = -1;
2566  *ub_cmpval = -1;
2567  return false;
2568  }
2569 
2570  /*
2571  * Check if the outer partition's lower bound is higher than the inner
2572  * partition's upper bound; if so the partitions aren't overlapping.
2573  */
2574  if (compare_range_bounds(partnatts, partsupfuncs, partcollations,
2575  outer_lb, inner_ub) > 0)
2576  {
2577  *lb_cmpval = 1;
2578  *ub_cmpval = 1;
2579  return false;
2580  }
2581 
2582  /* All other cases indicate overlapping partitions. */
2583  *lb_cmpval = compare_range_bounds(partnatts, partsupfuncs, partcollations,
2584  outer_lb, inner_lb);
2585  *ub_cmpval = compare_range_bounds(partnatts, partsupfuncs, partcollations,
2586  outer_ub, inner_ub);
2587  return true;
2588 }
#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 4621 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().

4623 {
4624  int i;
4625  uint64 rowHash = 0;
4627 
4628  for (i = 0; i < partnatts; i++)
4629  {
4630  /* Nulls are just ignored */
4631  if (!isnull[i])
4632  {
4633  Datum hash;
4634 
4635  Assert(OidIsValid(partsupfunc[i].fn_oid));
4636 
4637  /*
4638  * Compute hash for each datum value by calling respective
4639  * datatype-specific hash functions of each partition key
4640  * attribute.
4641  */
4642  hash = FunctionCall2Coll(&partsupfunc[i], partcollation[i],
4643  values[i], seed);
4644 
4645  /* Form a single 64-bit hash value */
4646  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
4647  }
4648  }
4649 
4650  return rowHash;
4651 }
#define UInt64GetDatum(X)
Definition: postgres.h:692
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1148
static uint64 hash_combine64(uint64 a, uint64 b)
Definition: hashfn.h:80
#define OidIsValid(objectId)
Definition: c.h:710
uintptr_t Datum
Definition: postgres.h:411
#define DatumGetUInt64(X)
Definition: postgres.h:678
#define Assert(condition)
Definition: c.h:804
#define HASH_PARTITION_SEED
Definition: partition.h:20
static Datum values[MAXATTR]
Definition: bootstrap.c:166
int i
static unsigned hash(unsigned *uv, int n)
Definition: rege_dfa.c:719

◆ create_hash_bounds()

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

Definition at line 357 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::nindexes, 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().

359 {
360  PartitionBoundInfo boundinfo;
361  PartitionHashBound **hbounds = NULL;
362  int i;
363  int ndatums = 0;
364  int greatest_modulus;
365 
366  boundinfo = (PartitionBoundInfoData *)
368  boundinfo->strategy = key->strategy;
369  /* No special hash partitions. */
370  boundinfo->null_index = -1;
371  boundinfo->default_index = -1;
372 
373  ndatums = nparts;
374  hbounds = (PartitionHashBound **)
375  palloc(nparts * sizeof(PartitionHashBound *));
376 
377  /* Convert from node to the internal representation */
378  for (i = 0; i < nparts; i++)
379  {
380  PartitionBoundSpec *spec = boundspecs[i];
381 
382  if (spec->strategy != PARTITION_STRATEGY_HASH)
383  elog(ERROR, "invalid strategy in partition bound spec");
384 
385  hbounds[i] = (PartitionHashBound *) palloc(sizeof(PartitionHashBound));
386  hbounds[i]->modulus = spec->modulus;
387  hbounds[i]->remainder = spec->remainder;
388  hbounds[i]->index = i;
389  }
390 
391  /* Sort all the bounds in ascending order */
392  qsort(hbounds, nparts, sizeof(PartitionHashBound *),
394 
395  /* After sorting, moduli are now stored in ascending order. */
396  greatest_modulus = hbounds[ndatums - 1]->modulus;
397 
398  boundinfo->ndatums = ndatums;
399  boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *));
400  boundinfo->nindexes = greatest_modulus;
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:3673
void pfree(void *pointer)
Definition: mcxt.c:1169
#define ERROR
Definition: elog.h:46
void * palloc0(Size size)
Definition: mcxt.c:1093
uintptr_t Datum
Definition: postgres.h:411
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:814
#define Assert(condition)
Definition: c.h:804
#define Int32GetDatum(X)
Definition: postgres.h:523
void * palloc(Size size)
Definition: mcxt.c:1062
#define elog(elevel,...)
Definition: elog.h:232
int i
#define qsort(a, b, c, d)
Definition: port.h:504

◆ 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::nindexes, 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->nindexes = ndatums;
534  boundinfo->indexes = (int *) palloc(ndatums * sizeof(int));
535 
536  /*
537  * Copy values. Canonical indexes are values ranging from 0 to (nparts -
538  * 1) assigned to each partition such that all datums of a given partition
539  * receive the same value. The value for a given partition is the index of
540  * that partition's smallest datum in the all_values[] array.
541  */
542  for (i = 0; i < ndatums; i++)
543  {
544  int orig_index = all_values[i]->index;
545 
546  boundinfo->datums[i] = (Datum *) palloc(sizeof(Datum));
547  boundinfo->datums[i][0] = datumCopy(all_values[i]->value,
548  key->parttypbyval[0],
549  key->parttyplen[0]);
550 
551  /* If the old index has no mapping, assign one */
552  if ((*mapping)[orig_index] == -1)
553  (*mapping)[orig_index] = next_index++;
554 
555  boundinfo->indexes[i] = (*mapping)[orig_index];
556  }
557 
558  /*
559  * Set the canonical value for null_index, if any.
560  *
561  * It is possible that the null-accepting partition has not been assigned
562  * an index yet, which could happen if such partition accepts only null
563  * and hence not handled in the above loop which only looked at non-null
564  * values.
565  */
566  if (null_index != -1)
567  {
568  Assert(null_index >= 0);
569  if ((*mapping)[null_index] == -1)
570  (*mapping)[null_index] = next_index++;
571  boundinfo->null_index = (*mapping)[null_index];
572  }
573 
574  /* Set the canonical value for default_index, if any. */
575  if (default_index != -1)
576  {
577  /*
578  * The default partition accepts any value not specified in the lists
579  * of other partitions, hence it should not get mapped index while
580  * assigning those for non-null datums.
581  */
582  Assert(default_index >= 0);
583  Assert((*mapping)[default_index] == -1);
584  (*mapping)[default_index] = next_index++;
585  boundinfo->default_index = (*mapping)[default_index];
586  }
587 
588  /* All partitions must now have been assigned canonical indexes. */
589  Assert(next_index == nparts);
590  return boundinfo;
591 }
Datum constvalue
Definition: primnodes.h:219
#define NIL
Definition: pg_list.h:65
static struct @142 value
#define castNode(_type_, nodeptr)
Definition: nodes.h:608
#define ERROR
Definition: elog.h:46
char * c
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:131
List * lappend(List *list, void *datum)
Definition: list.c:336
void qsort_arg(void *base, size_t nel, size_t elsize, qsort_arg_comparator cmp, void *arg)
void * palloc0(Size size)
Definition: mcxt.c:1093
uintptr_t Datum
Definition: postgres.h:411
bool * parttypbyval
Definition: partcache.h:44
#define Assert(condition)
Definition: c.h:804
#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:815
void * palloc(Size size)
Definition: mcxt.c:1062
#define elog(elevel,...)
Definition: elog.h:232
int i
static int32 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
Definition: partbounds.c:3688
Definition: pg_list.h:50
long val
Definition: informix.c:664
bool constisnull
Definition: primnodes.h:220

◆ create_range_bounds()

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

Definition at line 598 of file partbounds.c.

References Assert, cur, datumCopy(), DatumGetInt32, PartitionRangeBound::datums, PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, elog, ERROR, FunctionCall2Coll(), i, PartitionRangeBound::index, PartitionBoundInfoData::indexes, PartitionBoundSpec::is_default, PartitionRangeBound::kind, PartitionBoundInfoData::kind, lower(), PartitionBoundSpec::lowerdatums, make_one_partition_rbound(), PartitionBoundInfoData::ndatums, PartitionBoundInfoData::nindexes, 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().

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

2278 {
2279  int *new_indexes;
2280  int merged_index;
2281  int i;
2282  ListCell *lc;
2283 
2284  Assert(nmerged > 0);
2285 
2286  new_indexes = (int *) palloc(sizeof(int) * nmerged);
2287  for (i = 0; i < nmerged; i++)
2288  new_indexes[i] = -1;
2289 
2290  /* Build the mapping of old merged indexes to new merged indexes. */
2291  if (outer_map->did_remapping)
2292  {
2293  for (i = 0; i < outer_map->nparts; i++)
2294  {
2295  merged_index = outer_map->old_indexes[i];
2296  if (merged_index >= 0)
2297  new_indexes[merged_index] = outer_map->merged_indexes[i];
2298  }
2299  }
2300  if (inner_map->did_remapping)
2301  {
2302  for (i = 0; i < inner_map->nparts; i++)
2303  {
2304  merged_index = inner_map->old_indexes[i];
2305  if (merged_index >= 0)
2306  new_indexes[merged_index] = inner_map->merged_indexes[i];
2307  }
2308  }
2309 
2310  /* Fix the merged_indexes list using the mapping. */
2311  foreach(lc, merged_indexes)
2312  {
2313  merged_index = lfirst_int(lc);
2314  Assert(merged_index >= 0);
2315  if (new_indexes[merged_index] >= 0)
2316  lfirst_int(lc) = new_indexes[merged_index];
2317  }
2318 
2319  pfree(new_indexes);
2320 }
bool did_remapping
Definition: partbounds.c:82
void pfree(void *pointer)
Definition: mcxt.c:1169
#define lfirst_int(lc)
Definition: pg_list.h:170
#define Assert(condition)
Definition: c.h:804
void * palloc(Size size)
Definition: mcxt.c:1062
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 1723 of file partbounds.c.

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

Referenced by merge_list_bounds(), and merge_range_bounds().

1724 {
1725  pfree(map->merged_indexes);
1726  pfree(map->merged);
1727  pfree(map->old_indexes);
1728 }
void pfree(void *pointer)
Definition: mcxt.c:1169
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 2330 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().

2334 {
2335  int outer_nparts = outer_map->nparts;
2336  int inner_nparts = inner_map->nparts;
2337  int *outer_indexes;
2338  int *inner_indexes;
2339  int max_nparts;
2340  int i;
2341 
2342  Assert(nmerged > 0);
2343  Assert(*outer_parts == NIL);
2344  Assert(*inner_parts == NIL);
2345 
2346  outer_indexes = (int *) palloc(sizeof(int) * nmerged);
2347  inner_indexes = (int *) palloc(sizeof(int) * nmerged);
2348  for (i = 0; i < nmerged; i++)
2349  outer_indexes[i] = inner_indexes[i] = -1;
2350 
2351  /* Set pairs of matching partitions. */
2352  Assert(outer_nparts == outer_rel->nparts);
2353  Assert(inner_nparts == inner_rel->nparts);
2354  max_nparts = Max(outer_nparts, inner_nparts);
2355  for (i = 0; i < max_nparts; i++)
2356  {
2357  if (i < outer_nparts)
2358  {
2359  int merged_index = outer_map->merged_indexes[i];
2360 
2361  if (merged_index >= 0)
2362  {
2363  Assert(merged_index < nmerged);
2364  outer_indexes[merged_index] = i;
2365  }
2366  }
2367  if (i < inner_nparts)
2368  {
2369  int merged_index = inner_map->merged_indexes[i];
2370 
2371  if (merged_index >= 0)
2372  {
2373  Assert(merged_index < nmerged);
2374  inner_indexes[merged_index] = i;
2375  }
2376  }
2377  }
2378 
2379  /* Build the list pairs. */
2380  for (i = 0; i < nmerged; i++)
2381  {
2382  int outer_index = outer_indexes[i];
2383  int inner_index = inner_indexes[i];
2384 
2385  /*
2386  * If both partitions are dummy, it means the merged partition that
2387  * had been assigned to the outer/inner partition was removed when
2388  * re-merging the outer/inner partition in
2389  * merge_matching_partitions(); ignore the merged partition.
2390  */
2391  if (outer_index == -1 && inner_index == -1)
2392  continue;
2393 
2394  *outer_parts = lappend(*outer_parts, outer_index >= 0 ?
2395  outer_rel->part_rels[outer_index] : NULL);
2396  *inner_parts = lappend(*inner_parts, inner_index >= 0 ?
2397  inner_rel->part_rels[inner_index] : NULL);
2398  }
2399 
2400  pfree(outer_indexes);
2401  pfree(inner_indexes);
2402 }
#define NIL
Definition: pg_list.h:65
void pfree(void *pointer)
Definition: mcxt.c:1169
int nparts
Definition: pathnodes.h:756
List * lappend(List *list, void *datum)
Definition: list.c:336
#define Max(x, y)
Definition: c.h:980
#define Assert(condition)
Definition: c.h:804
struct RelOptInfo ** part_rels
Definition: pathnodes.h:763
void * palloc(Size size)
Definition: mcxt.c:1062
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 3309 of file partbounds.c.

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

3310 {
3311  Assert(bound && bound->strategy == PARTITION_STRATEGY_HASH);
3312  return bound->nindexes;
3313 }
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:814
#define Assert(condition)
Definition: c.h:804

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

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

◆ get_partition_operator()

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

Definition at line 3727 of file partbounds.c.

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

Referenced by make_partition_op_expr().

3729 {
3730  Oid operoid;
3731 
3732  /*
3733  * Get the operator in the partitioning opfamily using the opclass'
3734  * declared input type as both left- and righttype.
3735  */
3736  operoid = get_opfamily_member(key->partopfamily[col],
3737  key->partopcintype[col],
3738  key->partopcintype[col],
3739  strategy);
3740  if (!OidIsValid(operoid))
3741  elog(ERROR, "missing operator %d(%u,%u) in partition opfamily %u",
3742  strategy, key->partopcintype[col], key->partopcintype[col],
3743  key->partopfamily[col]);
3744 
3745  /*
3746  * If the partition key column is not of the same type as the operator
3747  * class and not polymorphic, tell caller to wrap the non-Const expression
3748  * in a RelabelType. This matches what parse_coerce.c does.
3749  */
3750  *need_relabel = (key->parttypid[col] != key->partopcintype[col] &&
3751  key->partopcintype[col] != RECORDOID &&
3752  !IsPolymorphicType(key->partopcintype[col]));
3753 
3754  return operoid;
3755 }
Oid * partopfamily
Definition: partcache.h:33
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:710
#define ERROR
Definition: elog.h:46
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:232

◆ get_qual_for_hash()

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

Definition at line 3877 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().

3878 {
3880  FuncExpr *fexpr;
3881  Node *relidConst;
3882  Node *modulusConst;
3883  Node *remainderConst;
3884  List *args;
3885  ListCell *partexprs_item;
3886  int i;
3887 
3888  /* Fixed arguments. */
3889  relidConst = (Node *) makeConst(OIDOID,
3890  -1,
3891  InvalidOid,
3892  sizeof(Oid),
3894  false,
3895  true);
3896 
3897  modulusConst = (Node *) makeConst(INT4OID,
3898  -1,
3899  InvalidOid,
3900  sizeof(int32),
3901  Int32GetDatum(spec->modulus),
3902  false,
3903  true);
3904 
3905  remainderConst = (Node *) makeConst(INT4OID,
3906  -1,
3907  InvalidOid,
3908  sizeof(int32),
3909  Int32GetDatum(spec->remainder),
3910  false,
3911  true);
3912 
3913  args = list_make3(relidConst, modulusConst, remainderConst);
3914  partexprs_item = list_head(key->partexprs);
3915 
3916  /* Add an argument for each key column. */
3917  for (i = 0; i < key->partnatts; i++)
3918  {
3919  Node *keyCol;
3920 
3921  /* Left operand */
3922  if (key->partattrs[i] != 0)
3923  {
3924  keyCol = (Node *) makeVar(1,
3925  key->partattrs[i],
3926  key->parttypid[i],
3927  key->parttypmod[i],
3928  key->parttypcoll[i],
3929  0);
3930  }
3931  else
3932  {
3933  keyCol = (Node *) copyObject(lfirst(partexprs_item));
3934  partexprs_item = lnext(key->partexprs, partexprs_item);
3935  }
3936 
3937  args = lappend(args, keyCol);
3938  }
3939 
3940  fexpr = makeFuncExpr(F_SATISFIES_HASH_PARTITION,
3941  BOOLOID,
3942  args,
3943  InvalidOid,
3944  InvalidOid,
3946 
3947  return list_make1(fexpr);
3948 }
#define list_make3(x1, x2, x3)
Definition: pg_list.h:210
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:322
Definition: nodes.h:539
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:429
#define list_make1(x1)
Definition: pg_list.h:206
Oid * parttypcoll
Definition: partcache.h:46
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
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:336
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:523
int i
#define copyObject(obj)
Definition: nodes.h:655
Definition: pg_list.h:50
#define RelationGetRelid(relation)
Definition: rel.h:469
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 3960 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().

3961 {
3963  List *result;
3964  Expr *keyCol;
3965  Expr *opexpr;
3966  NullTest *nulltest;
3967  ListCell *cell;
3968  List *elems = NIL;
3969  bool list_has_null = false;
3970 
3971  /*
3972  * Only single-column list partitioning is supported, so we are worried
3973  * only about the partition key with index 0.
3974  */
3975  Assert(key->partnatts == 1);
3976 
3977  /* Construct Var or expression representing the partition column */
3978  if (key->partattrs[0] != 0)
3979  keyCol = (Expr *) makeVar(1,
3980  key->partattrs[0],
3981  key->parttypid[0],
3982  key->parttypmod[0],
3983  key->parttypcoll[0],
3984  0);
3985  else
3986  keyCol = (Expr *) copyObject(linitial(key->partexprs));
3987 
3988  /*
3989  * For default list partition, collect datums for all the partitions. The
3990  * default partition constraint should check that the partition key is
3991  * equal to none of those.
3992  */
3993  if (spec->is_default)
3994  {
3995  int i;
3996  int ndatums = 0;
3997  PartitionDesc pdesc = RelationGetPartitionDesc(parent, false);
3998  PartitionBoundInfo boundinfo = pdesc->boundinfo;
3999 
4000  if (boundinfo)
4001  {
4002  ndatums = boundinfo->ndatums;
4003 
4004  if (partition_bound_accepts_nulls(boundinfo))
4005  list_has_null = true;
4006  }
4007 
4008  /*
4009  * If default is the only partition, there need not be any partition
4010  * constraint on it.
4011  */
4012  if (ndatums == 0 && !list_has_null)
4013  return NIL;
4014 
4015  for (i = 0; i < ndatums; i++)
4016  {
4017  Const *val;
4018 
4019  /*
4020  * Construct Const from known-not-null datum. We must be careful
4021  * to copy the value, because our result has to be able to outlive
4022  * the relcache entry we're copying from.
4023  */
4024  val = makeConst(key->parttypid[0],
4025  key->parttypmod[0],
4026  key->parttypcoll[0],
4027  key->parttyplen[0],
4028  datumCopy(*boundinfo->datums[i],
4029  key->parttypbyval[0],
4030  key->parttyplen[0]),
4031  false, /* isnull */
4032  key->parttypbyval[0]);
4033 
4034  elems = lappend(elems, val);
4035  }
4036  }
4037  else
4038  {
4039  /*
4040  * Create list of Consts for the allowed values, excluding any nulls.
4041  */
4042  foreach(cell, spec->listdatums)
4043  {
4044  Const *val = castNode(Const, lfirst(cell));
4045 
4046  if (val->constisnull)
4047  list_has_null = true;
4048  else
4049  elems = lappend(elems, copyObject(val));
4050  }
4051  }
4052 
4053  if (elems)
4054  {
4055  /*
4056  * Generate the operator expression from the non-null partition
4057  * values.
4058  */
4060  keyCol, (Expr *) elems);
4061  }
4062  else
4063  {
4064  /*
4065  * If there are no partition values, we don't need an operator
4066  * expression.
4067  */
4068  opexpr = NULL;
4069  }
4070 
4071  if (!list_has_null)
4072  {
4073  /*
4074  * Gin up a "col IS NOT NULL" test that will be ANDed with the main
4075  * expression. This might seem redundant, but the partition routing
4076  * machinery needs it.
4077  */
4078  nulltest = makeNode(NullTest);
4079  nulltest->arg = keyCol;
4080  nulltest->nulltesttype = IS_NOT_NULL;
4081  nulltest->argisrow = false;
4082  nulltest->location = -1;
4083 
4084  result = opexpr ? list_make2(nulltest, opexpr) : list_make1(nulltest);
4085  }
4086  else
4087  {
4088  /*
4089  * Gin up a "col IS NULL" test that will be OR'd with the main
4090  * expression.
4091  */
4092  nulltest = makeNode(NullTest);
4093  nulltest->arg = keyCol;
4094  nulltest->nulltesttype = IS_NULL;
4095  nulltest->argisrow = false;
4096  nulltest->location = -1;
4097 
4098  if (opexpr)
4099  {
4100  Expr *or;
4101 
4102  or = makeBoolExpr(OR_EXPR, list_make2(nulltest, opexpr), -1);
4103  result = list_make1(or);
4104  }
4105  else
4106  result = list_make1(nulltest);
4107  }
4108 
4109  /*
4110  * Note that, in general, applying NOT to a constraint expression doesn't
4111  * necessarily invert the set of rows it accepts, because NOT (NULL) is
4112  * NULL. However, the partition constraints we construct here never
4113  * evaluate to NULL, so applying NOT works as intended.
4114  */
4115  if (spec->is_default)
4116  {
4117  result = list_make1(make_ands_explicit(result));
4118  result = list_make1(makeBoolExpr(NOT_EXPR, result, -1));
4119  }
4120 
4121  return result;
4122 }
#define list_make2(x1, x2)
Definition: pg_list.h:208
#define NIL
Definition: pg_list.h:65
#define castNode(_type_, nodeptr)
Definition: nodes.h:608
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:38
#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:3763
Oid * parttypcoll
Definition: partcache.h:46
#define linitial(l)
Definition: pg_list.h:174
Expr * arg
Definition: primnodes.h:1255
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition: partdesc.c:72
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:336
AttrNumber * partattrs
Definition: partcache.h:28
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:708
NullTestType nulltesttype
Definition: primnodes.h:1256
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:81
int32 * parttypmod
Definition: partcache.h:42
#define makeNode(_type_)
Definition: nodes.h:587
bool * parttypbyval
Definition: partcache.h:44
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
int16 * parttyplen
Definition: partcache.h:43
int location
Definition: primnodes.h:1258
int i
bool argisrow
Definition: primnodes.h:1257
#define copyObject(obj)
Definition: nodes.h:655
Definition: pg_list.h:50
long val
Definition: informix.c:664
bool constisnull
Definition: primnodes.h:220
#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 4169 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().

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

251 {
253  List *my_qual = NIL;
254 
255  Assert(key != NULL);
256 
257  switch (key->strategy)
258  {
261  my_qual = get_qual_for_hash(parent, spec);
262  break;
263 
266  my_qual = get_qual_for_list(parent, spec);
267  break;
268 
271  my_qual = get_qual_for_range(parent, spec, false);
272  break;
273 
274  default:
275  elog(ERROR, "unexpected partition strategy: %d",
276  (int) key->strategy);
277  }
278 
279  return my_qual;
280 }
#define NIL
Definition: pg_list.h:65
static List * get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:3877
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
#define ERROR
Definition: elog.h:46
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:814
static List * get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default)
Definition: partbounds.c:4169
#define Assert(condition)
Definition: c.h:804
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:815
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:816
#define elog(elevel,...)
Definition: elog.h:232
Definition: pg_list.h:50
static List * get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:3960

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

4537 {
4538  /* Get partition key expression for this column */
4539  if (key->partattrs[keynum] != 0)
4540  {
4541  *keyCol = (Expr *) makeVar(1,
4542  key->partattrs[keynum],
4543  key->parttypid[keynum],
4544  key->parttypmod[keynum],
4545  key->parttypcoll[keynum],
4546  0);
4547  }
4548  else
4549  {
4550  if (*partexprs_item == NULL)
4551  elog(ERROR, "wrong number of partition key expressions");
4552  *keyCol = copyObject(lfirst(*partexprs_item));
4553  *partexprs_item = lnext(key->partexprs, *partexprs_item);
4554  }
4555 
4556  /* Get appropriate Const nodes for the bounds */
4557  if (ldatum->kind == PARTITION_RANGE_DATUM_VALUE)
4558  *lower_val = castNode(Const, copyObject(ldatum->value));
4559  else
4560  *lower_val = NULL;
4561 
4562  if (udatum->kind == PARTITION_RANGE_DATUM_VALUE)
4563  *upper_val = castNode(Const, copyObject(udatum->value));
4564  else
4565  *upper_val = NULL;
4566 }
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:322
#define castNode(_type_, nodeptr)
Definition: nodes.h:608
PartitionRangeDatumKind kind
Definition: parsenodes.h:861
List * partexprs
Definition: partcache.h:30
Oid * parttypcoll
Definition: partcache.h:46
#define ERROR
Definition: elog.h:46
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:232
#define copyObject(obj)
Definition: nodes.h:655

◆ get_range_nulltest()

static List * get_range_nulltest ( PartitionKey  key)
static

Definition at line 4575 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().

4576 {
4577  List *result = NIL;
4578  NullTest *nulltest;
4579  ListCell *partexprs_item;
4580  int i;
4581 
4582  partexprs_item = list_head(key->partexprs);
4583  for (i = 0; i < key->partnatts; i++)
4584  {
4585  Expr *keyCol;
4586 
4587  if (key->partattrs[i] != 0)
4588  {
4589  keyCol = (Expr *) makeVar(1,
4590  key->partattrs[i],
4591  key->parttypid[i],
4592  key->parttypmod[i],
4593  key->parttypcoll[i],
4594  0);
4595  }
4596  else
4597  {
4598  if (partexprs_item == NULL)
4599  elog(ERROR, "wrong number of partition key expressions");
4600  keyCol = copyObject(lfirst(partexprs_item));
4601  partexprs_item = lnext(key->partexprs, partexprs_item);
4602  }
4603 
4604  nulltest = makeNode(NullTest);
4605  nulltest->arg = keyCol;
4606  nulltest->nulltesttype = IS_NOT_NULL;
4607  nulltest->argisrow = false;
4608  nulltest->location = -1;
4609  result = lappend(result, nulltest);
4610  }
4611 
4612  return result;
4613 }
#define NIL
Definition: pg_list.h:65
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:322
List * partexprs
Definition: partcache.h:30
Oid * parttypcoll
Definition: partcache.h:46
#define ERROR
Definition: elog.h:46
Expr * arg
Definition: primnodes.h:1255
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:336
AttrNumber * partattrs
Definition: partcache.h:28
NullTestType nulltesttype
Definition: primnodes.h:1256
int32 * parttypmod
Definition: partcache.h:42
#define makeNode(_type_)
Definition: nodes.h:587
#define lfirst(lc)
Definition: pg_list.h:169
int location
Definition: primnodes.h:1258
#define elog(elevel,...)
Definition: elog.h:232
int i
bool argisrow
Definition: primnodes.h:1257
#define copyObject(obj)
Definition: nodes.h:655
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 2469 of file partbounds.c.

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

Referenced by merge_range_bounds().

2474 {
2475  int part_index;
2476 
2478 
2479  do
2480  {
2481  part_index = get_range_partition_internal(bi, lb_pos, lb, ub);
2482  if (part_index == -1)
2483  return -1;
2484  } while (is_dummy_partition(rel, part_index));
2485 
2486  return part_index;
2487 }
static int get_range_partition_internal(PartitionBoundInfo bi, int *lb_pos, PartitionRangeBound *lb, PartitionRangeBound *ub)
Definition: partbounds.c:2490
static bool is_dummy_partition(RelOptInfo *rel, int part_index)
Definition: partbounds.c:1734
#define Assert(condition)
Definition: c.h:804
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:816

◆ get_range_partition_internal()

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

Definition at line 2490 of file partbounds.c.

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

Referenced by get_range_partition().

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

◆ init_partition_map()

static void init_partition_map ( RelOptInfo rel,
PartitionMap map 
)
static

Definition at line 1702 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().

1703 {
1704  int nparts = rel->nparts;
1705  int i;
1706 
1707  map->nparts = nparts;
1708  map->merged_indexes = (int *) palloc(sizeof(int) * nparts);
1709  map->merged = (bool *) palloc(sizeof(bool) * nparts);
1710  map->did_remapping = false;
1711  map->old_indexes = (int *) palloc(sizeof(int) * nparts);
1712  for (i = 0; i < nparts; i++)
1713  {
1714  map->merged_indexes[i] = map->old_indexes[i] = -1;
1715  map->merged[i] = false;
1716  }
1717 }
bool did_remapping
Definition: partbounds.c:82
int nparts
Definition: pathnodes.h:756
bool * merged
Definition: partbounds.c:80
void * palloc(Size size)
Definition: mcxt.c:1062
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 1734 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().

1735 {
1736  RelOptInfo *part_rel;
1737 
1738  Assert(part_index >= 0);
1739  part_rel = rel->part_rels[part_index];
1740  if (part_rel == NULL || IS_DUMMY_REL(part_rel))
1741  return true;
1742  return false;
1743 }
#define IS_DUMMY_REL(r)
Definition: pathnodes.h:1458
#define Assert(condition)
Definition: c.h:804
struct RelOptInfo ** part_rels
Definition: pathnodes.h:763

◆ make_one_partition_rbound()

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

Definition at line 3323 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().

3324 {
3325  PartitionRangeBound *bound;
3326  ListCell *lc;
3327  int i;
3328 
3329  Assert(datums != NIL);
3330 
3331  bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
3332  bound->index = index;
3333  bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
3334  bound->kind = (PartitionRangeDatumKind *) palloc0(key->partnatts *
3335  sizeof(PartitionRangeDatumKind));
3336  bound->lower = lower;
3337 
3338  i = 0;
3339  foreach(lc, datums)
3340  {
3342 
3343  /* What's contained in this range datum? */
3344  bound->kind[i] = datum->kind;
3345 
3346  if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
3347  {
3348  Const *val = castNode(Const, datum->value);
3349 
3350  if (val->constisnull)
3351  elog(ERROR, "invalid range bound datum");
3352  bound->datums[i] = val->constvalue;
3353  }
3354 
3355  i++;
3356  }
3357 
3358  return bound;
3359 }
Datum constvalue
Definition: primnodes.h:219
#define NIL
Definition: pg_list.h:65
PartitionRangeDatumKind * kind
Definition: partbounds.c:68
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:46
#define castNode(_type_, nodeptr)
Definition: nodes.h:608
PartitionRangeDatumKind
Definition: parsenodes.h:850
PartitionRangeDatumKind kind
Definition: parsenodes.h:861
Definition: type.h:89
#define ERROR
Definition: elog.h:46
void * palloc0(Size size)
Definition: mcxt.c:1093
uintptr_t Datum
Definition: postgres.h:411
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
#define elog(elevel,...)
Definition: elog.h:232
int i
long val
Definition: informix.c:664
bool constisnull
Definition: primnodes.h:220

◆ make_partition_op_expr()

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

Definition at line 3763 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::hashfuncid, 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().

3765 {
3766  Oid operoid;
3767  bool need_relabel = false;
3768  Expr *result = NULL;
3769 
3770  /* Get the correct btree operator for this partitioning column */
3771  operoid = get_partition_operator(key, keynum, strategy, &need_relabel);
3772 
3773  /*
3774  * Chosen operator may be such that the non-Const operand needs to be
3775  * coerced, so apply the same; see the comment in
3776  * get_partition_operator().
3777  */
3778  if (!IsA(arg1, Const) &&
3779  (need_relabel ||
3780  key->partcollation[keynum] != key->parttypcoll[keynum]))
3781  arg1 = (Expr *) makeRelabelType(arg1,
3782  key->partopcintype[keynum],
3783  -1,
3784  key->partcollation[keynum],
3786 
3787  /* Generate the actual expression */
3788  switch (key->strategy)
3789  {
3791  {
3792  List *elems = (List *) arg2;
3793  int nelems = list_length(elems);
3794 
3795  Assert(nelems >= 1);
3796  Assert(keynum == 0);
3797 
3798  if (nelems > 1 &&
3799  !type_is_array(key->parttypid[keynum]))
3800  {
3801  ArrayExpr *arrexpr;
3802  ScalarArrayOpExpr *saopexpr;
3803 
3804  /* Construct an ArrayExpr for the right-hand inputs */
3805  arrexpr = makeNode(ArrayExpr);
3806  arrexpr->array_typeid =
3807  get_array_type(key->parttypid[keynum]);
3808  arrexpr->array_collid = key->parttypcoll[keynum];
3809  arrexpr->element_typeid = key->parttypid[keynum];
3810  arrexpr->elements = elems;
3811  arrexpr->multidims = false;
3812  arrexpr->location = -1;
3813 
3814  /* Build leftop = ANY (rightop) */
3815  saopexpr = makeNode(ScalarArrayOpExpr);
3816  saopexpr->opno = operoid;
3817  saopexpr->opfuncid = get_opcode(operoid);
3818  saopexpr->hashfuncid = InvalidOid;
3819  saopexpr->useOr = true;
3820  saopexpr->inputcollid = key->partcollation[keynum];
3821  saopexpr->args = list_make2(arg1, arrexpr);
3822  saopexpr->location = -1;
3823 
3824  result = (Expr *) saopexpr;
3825  }
3826  else
3827  {
3828  List *elemops = NIL;
3829  ListCell *lc;
3830 
3831  foreach(lc, elems)
3832  {
3833  Expr *elem = lfirst(lc),
3834  *elemop;
3835 
3836  elemop = make_opclause(operoid,
3837  BOOLOID,
3838  false,
3839  arg1, elem,
3840  InvalidOid,
3841  key->partcollation[keynum]);
3842  elemops = lappend(elemops, elemop);
3843  }
3844 
3845  result = nelems > 1 ? makeBoolExpr(OR_EXPR, elemops, -1) : linitial(elemops);
3846  }
3847  break;
3848  }
3849 
3851  result = make_opclause(operoid,
3852  BOOLOID,
3853  false,
3854  arg1, arg2,
3855  InvalidOid,
3856  key->partcollation[keynum]);
3857  break;
3858 
3859  default:
3860  elog(ERROR, "invalid partitioning strategy");
3861  break;
3862  }
3863 
3864  return result;
3865 }
#define list_make2(x1, x2)
Definition: pg_list.h:208
bool multidims
Definition: primnodes.h:1028
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:590
Oid get_array_type(Oid typid)
Definition: lsyscache.c:2734
Oid array_typeid
Definition: primnodes.h:1024
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:610
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:46
RelabelType * makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod, Oid rcollid, CoercionForm rformat)
Definition: makefuncs.c:402
List * elements
Definition: primnodes.h:1027
List * lappend(List *list, void *datum)
Definition: list.c:336
Oid * partcollation
Definition: partcache.h:38
int location
Definition: primnodes.h:1029
static Oid get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel)
Definition: partbounds.c:3727
#define InvalidOid
Definition: postgres_ext.h:36
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1256
#define makeNode(_type_)
Definition: nodes.h:587
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
Oid array_collid
Definition: primnodes.h:1025
static int list_length(const List *l)
Definition: pg_list.h:149
#define type_is_array(typid)
Definition: lsyscache.h:202
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:815
Oid element_typeid
Definition: primnodes.h:1026
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:816
Oid * partopcintype
Definition: partcache.h:34
#define elog(elevel,...)
Definition: elog.h:232
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 2148 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().

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

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

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

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

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

2259 {
2260  int merged_index = *next_index;
2261 
2262  Assert(index >= 0 && index < map->nparts);
2263  Assert(map->merged_indexes[index] == -1);
2264  Assert(!map->merged[index]);
2265  map->merged_indexes[index] = merged_index;
2266  /* Leave the merged flag alone! */
2267  *next_index = *next_index + 1;
2268  return merged_index;
2269 }
Definition: type.h:89
bool * merged
Definition: partbounds.c:80
#define Assert(condition)
Definition: c.h:804
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 1397 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().

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

◆ partition_bounds_copy()

PartitionBoundInfo partition_bounds_copy ( PartitionBoundInfo  src,
PartitionKey  key 
)

Definition at line 907 of file partbounds.c.

References Assert, datumCopy(), PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, generate_unaccent_rules::dest, i, PartitionBoundInfoData::indexes, PartitionBoundInfoData::kind, PartitionBoundInfoData::ndatums, PartitionBoundInfoData::nindexes, 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().

909 {
911  int i;
912  int ndatums;
913  int nindexes;
914  int partnatts;
915  bool hash_part;
916  int natts;
917 
919 
920  dest->strategy = src->strategy;
921  ndatums = dest->ndatums = src->ndatums;
922  nindexes = dest->nindexes = src->nindexes;
923  partnatts = key->partnatts;
924 
925  /* List partitioned tables have only a single partition key. */
926  Assert(key->strategy != PARTITION_STRATEGY_LIST || partnatts == 1);
927 
928  dest->datums = (Datum **) palloc(sizeof(Datum *) * ndatums);
929 
930  if (src->kind != NULL)
931  {
932  dest->kind = (PartitionRangeDatumKind **) palloc(ndatums *
933  sizeof(PartitionRangeDatumKind *));
934  for (i = 0; i < ndatums; i++)
935  {
936  dest->kind[i] = (PartitionRangeDatumKind *) palloc(partnatts *
937  sizeof(PartitionRangeDatumKind));
938 
939  memcpy(dest->kind[i], src->kind[i],
940  sizeof(PartitionRangeDatumKind) * key->partnatts);
941  }
942  }
943  else
944  dest->kind = NULL;
945 
946  /*
947  * For hash partitioning, datums array will have two elements - modulus
948  * and remainder.
949  */
950  hash_part = (key->strategy == PARTITION_STRATEGY_HASH);
951  natts = hash_part ? 2 : partnatts;
952 
953  for (i = 0; i < ndatums; i++)
954  {
955  int j;
956 
957  dest->datums[i] = (Datum *) palloc(sizeof(Datum) * natts);
958 
959  for (j = 0; j < natts; j++)
960  {
961  bool byval;
962  int typlen;
963 
964  if (hash_part)
965  {
966  typlen = sizeof(int32); /* Always int4 */
967  byval = true; /* int4 is pass-by-value */
968  }
969  else
970  {
971  byval = key->parttypbyval[j];
972  typlen = key->parttyplen[j];
973  }
974 
975  if (dest->kind == NULL ||
976  dest->kind[i][j] == PARTITION_RANGE_DATUM_VALUE)
977  dest->datums[i][j] = datumCopy(src->datums[i][j],
978  byval, typlen);
979  }
980  }
981 
982  dest->indexes = (int *) palloc(sizeof(int) * nindexes);
983  memcpy(dest->indexes, src->indexes, sizeof(int) * nindexes);
984 
985  dest->null_index = src->null_index;
986  dest->default_index = src->default_index;
987 
988  return dest;
989 }
PartitionRangeDatumKind ** kind
Definition: partbounds.h:70
PartitionRangeDatumKind
Definition: parsenodes.h:850
signed int int32
Definition: c.h:429
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:131
uintptr_t Datum
Definition: postgres.h:411
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:814
bool * parttypbyval
Definition: partcache.h:44
#define Assert(condition)
Definition: c.h:804
int16 * parttyplen
Definition: partcache.h:43
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:815
void * palloc(Size size)
Definition: mcxt.c:1062
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 304 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().

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

◆ partition_bounds_equal()

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

Definition at line 801 of file partbounds.c.

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

Referenced by compute_partition_bounds().

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

◆ partition_bounds_merge()

PartitionBoundInfo partition_bounds_merge ( int  partnatts,
FmgrInfo partsupfunc,
Oid partcollation,
RelOptInfo outer_rel,
RelOptInfo inner_rel,
JoinType  jointype,