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:3380

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:837
#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:367
static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partbounds.c:3380
#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:837
#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:367
#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:802
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
void * palloc(Size size)
Definition: mcxt.c:950
struct PartitionBoundInfoData * PartitionBoundInfo
Definition: partdefs.h:16

◆ check_default_partition_contents()

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

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

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

◆ 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);
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  /* We found the largest modulus less than or equal to ours. */
2880  prev_modulus = DatumGetInt32(boundinfo->datums[offset][0]);
2881 
2882  if (spec->modulus % prev_modulus != 0)
2883  ereport(ERROR,
2884  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2885  errmsg("every hash partition modulus must be a factor of the next larger modulus"),
2886  errdetail("The new modulus %d is not divisible by %d, the modulus of existing partition \"%s\".",
2887  spec->modulus,
2888  prev_modulus,
2889  get_rel_name(partdesc->oids[boundinfo->indexes[offset]]))));
2890 
2891  if (offset + 1 < boundinfo->ndatums)
2892  {
2893  int next_modulus;
2894 
2895  /* Look at the next higher modulus */
2896  next_modulus = DatumGetInt32(boundinfo->datums[offset + 1][0]);
2897 
2898  if (next_modulus % spec->modulus != 0)
2899  ereport(ERROR,
2900  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2901  errmsg("every hash partition modulus must be a factor of the next larger modulus"),
2902  errdetail("The new modulus %d is not factor of %d, the modulus of existing partition \"%s\".",
2903  spec->modulus, next_modulus,
2904  get_rel_name(partdesc->oids[boundinfo->indexes[offset + 1]]))));
2905  }
2906  }
2907 
2908  greatest_modulus = boundinfo->nindexes;
2909  remainder = spec->remainder;
2910 
2911  /*
2912  * Normally, the lowest remainder that could conflict with
2913  * the new partition is equal to the remainder specified
2914  * for the new partition, but when the new partition has a
2915  * modulus higher than any used so far, we need to adjust.
2916  */
2917  if (remainder >= greatest_modulus)
2918  remainder = remainder % greatest_modulus;
2919 
2920  /* Check every potentially-conflicting remainder. */
2921  do
2922  {
2923  if (boundinfo->indexes[remainder] != -1)
2924  {
2925  overlap = true;
2926  overlap_location = spec->location;
2927  with = boundinfo->indexes[remainder];
2928  break;
2929  }
2930  remainder += spec->modulus;
2931  } while (remainder < greatest_modulus);
2932  }
2933 
2934  break;
2935  }
2936 
2938  {
2940 
2941  if (partdesc->nparts > 0)
2942  {
2943  ListCell *cell;
2944 
2945  Assert(boundinfo &&
2946  boundinfo->strategy == PARTITION_STRATEGY_LIST &&
2947  (boundinfo->ndatums > 0 ||
2948  partition_bound_accepts_nulls(boundinfo) ||
2949  partition_bound_has_default(boundinfo)));
2950 
2951  foreach(cell, spec->listdatums)
2952  {
2953  Const *val = castNode(Const, lfirst(cell));
2954 
2955  overlap_location = val->location;
2956  if (!val->constisnull)
2957  {
2958  int offset;
2959  bool equal;
2960 
2961  offset = partition_list_bsearch(&key->partsupfunc[0],
2962  key->partcollation,
2963  boundinfo,
2964  val->constvalue,
2965  &equal);
2966  if (offset >= 0 && equal)
2967  {
2968  overlap = true;
2969  with = boundinfo->indexes[offset];
2970  break;
2971  }
2972  }
2973  else if (partition_bound_accepts_nulls(boundinfo))
2974  {
2975  overlap = true;
2976  with = boundinfo->null_index;
2977  break;
2978  }
2979  }
2980  }
2981 
2982  break;
2983  }
2984 
2986  {
2988  *upper;
2989  int cmpval;
2990 
2992  lower = make_one_partition_rbound(key, -1, spec->lowerdatums, true);
2993  upper = make_one_partition_rbound(key, -1, spec->upperdatums, false);
2994 
2995  /*
2996  * First check if the resulting range would be empty with
2997  * specified lower and upper bounds. partition_rbound_cmp
2998  * cannot return zero here, since the lower-bound flags are
2999  * different.
3000  */
3001  cmpval = partition_rbound_cmp(key->partnatts,
3002  key->partsupfunc,
3003  key->partcollation,
3004  lower->datums, lower->kind,
3005  true, upper);
3006  Assert(cmpval != 0);
3007  if (cmpval > 0)
3008  {
3009  /* Point to problematic key in the lower datums list. */
3010  PartitionRangeDatum *datum = list_nth(spec->lowerdatums,
3011  cmpval - 1);
3012 
3013  ereport(ERROR,
3014  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3015  errmsg("empty range bound specified for partition \"%s\"",
3016  relname),
3017  errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
3020  parser_errposition(pstate, datum->location)));
3021  }
3022 
3023  if (partdesc->nparts > 0)
3024  {
3025  int offset;
3026 
3027  Assert(boundinfo &&
3028  boundinfo->strategy == PARTITION_STRATEGY_RANGE &&
3029  (boundinfo->ndatums > 0 ||
3030  partition_bound_has_default(boundinfo)));
3031 
3032  /*
3033  * Test whether the new lower bound (which is treated
3034  * inclusively as part of the new partition) lies inside
3035  * an existing partition, or in a gap.
3036  *
3037  * If it's inside an existing partition, the bound at
3038  * offset + 1 will be the upper bound of that partition,
3039  * and its index will be >= 0.
3040  *
3041  * If it's in a gap, the bound at offset + 1 will be the
3042  * lower bound of the next partition, and its index will
3043  * be -1. This is also true if there is no next partition,
3044  * since the index array is initialised with an extra -1
3045  * at the end.
3046  */
3047  offset = partition_range_bsearch(key->partnatts,
3048  key->partsupfunc,
3049  key->partcollation,
3050  boundinfo, lower,
3051  &cmpval);
3052 
3053  if (boundinfo->indexes[offset + 1] < 0)
3054  {
3055  /*
3056  * Check that the new partition will fit in the gap.
3057  * For it to fit, the new upper bound must be less
3058  * than or equal to the lower bound of the next
3059  * partition, if there is one.
3060  */
3061  if (offset + 1 < boundinfo->ndatums)
3062  {
3063  Datum *datums;
3065  bool is_lower;
3066 
3067  datums = boundinfo->datums[offset + 1];
3068  kind = boundinfo->kind[offset + 1];
3069  is_lower = (boundinfo->indexes[offset + 1] == -1);
3070 
3071  cmpval = partition_rbound_cmp(key->partnatts,
3072  key->partsupfunc,
3073  key->partcollation,
3074  datums, kind,
3075  is_lower, upper);
3076  if (cmpval < 0)
3077  {
3078  /*
3079  * Point to problematic key in the upper
3080  * datums list.
3081  */
3082  PartitionRangeDatum *datum =
3083  list_nth(spec->upperdatums, Abs(cmpval) - 1);
3084 
3085  /*
3086  * The new partition overlaps with the
3087  * existing partition between offset + 1 and
3088  * offset + 2.
3089  */
3090  overlap = true;
3091  overlap_location = datum->location;
3092  with = boundinfo->indexes[offset + 2];
3093  }
3094  }
3095  }
3096  else
3097  {
3098  /*
3099  * The new partition overlaps with the existing
3100  * partition between offset and offset + 1.
3101  */
3102  PartitionRangeDatum *datum;
3103 
3104  /*
3105  * Point to problematic key in the lower datums list;
3106  * if we have equality, point to the first one.
3107  */
3108  datum = cmpval == 0 ? linitial(spec->lowerdatums) :
3109  list_nth(spec->lowerdatums, Abs(cmpval) - 1);
3110  overlap = true;
3111  overlap_location = datum->location;
3112  with = boundinfo->indexes[offset + 1];
3113  }
3114  }
3115 
3116  break;
3117  }
3118 
3119  default:
3120  elog(ERROR, "unexpected partition strategy: %d",
3121  (int) key->strategy);
3122  }
3123 
3124  if (overlap)
3125  {
3126  Assert(with >= 0);
3127  ereport(ERROR,
3128  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3129  errmsg("partition \"%s\" would overlap partition \"%s\"",
3130  relname, get_rel_name(partdesc->oids[with])),
3131  parser_errposition(pstate, overlap_location)));
3132  }
3133 }
Datum constvalue
Definition: primnodes.h:214
PartitionRangeDatumKind ** kind
Definition: partbounds.h:70
PartitionRangeDatumKind * kind
Definition: partbounds.c:68
#define DatumGetInt32(X)
Definition: postgres.h:472
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3073
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:46
FmgrInfo * partsupfunc
Definition: partcache.h:35
#define castNode(_type_, nodeptr)
Definition: nodes.h:602
PartitionRangeDatumKind
Definition: parsenodes.h:837
int errcode(int sqlerrcode)
Definition: elog.c:694
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:29
#define Abs(x)
Definition: c.h:992
static PartitionRangeBound * make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
Definition: partbounds.c:3320
#define linitial(l)
Definition: pg_list.h:174
#define ERROR
Definition: elog.h:45
static void * list_nth(const List *list, int n)
Definition: pg_list.h:278
int errdetail(const char *fmt,...)
Definition: elog.c:1038
PartitionDesc RelationGetPartitionDesc(Relation rel)
Definition: partdesc.c:64
#define partition_bound_has_default(bi)
Definition: partbounds.h:82
int partition_hash_bsearch(PartitionBoundInfo boundinfo, int modulus, int remainder)
Definition: partbounds.c:3630
char * get_range_partbound_string(List *bound_datums)
Definition: ruleutils.c:11640
Oid * partcollation
Definition: partcache.h:38
int location
Definition: primnodes.h:221
uintptr_t Datum
Definition: postgres.h:367
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:81
int partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
Definition: partbounds.c:3499
#define ereport(elevel,...)
Definition: elog.h:155
static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partbounds.c:3380
#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:3545
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
int errmsg(const char *fmt,...)
Definition: elog.c:905
#define elog(elevel,...)
Definition: elog.h:227
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1899
long val
Definition: informix.c:664
bool constisnull
Definition: primnodes.h:215

◆ compare_range_partitions()

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

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

4619 {
4620  int i;
4621  uint64 rowHash = 0;
4623 
4624  for (i = 0; i < partnatts; i++)
4625  {
4626  /* Nulls are just ignored */
4627  if (!isnull[i])
4628  {
4629  Datum hash;
4630 
4631  Assert(OidIsValid(partsupfunc[i].fn_oid));
4632 
4633  /*
4634  * Compute hash for each datum value by calling respective
4635  * datatype-specific hash functions of each partition key
4636  * attribute.
4637  */
4638  hash = FunctionCall2Coll(&partsupfunc[i], partcollation[i],
4639  values[i], seed);
4640 
4641  /* Form a single 64-bit hash value */
4642  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
4643  }
4644  }
4645 
4646  return rowHash;
4647 }
#define UInt64GetDatum(X)
Definition: postgres.h:648
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1151
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:367
#define DatumGetUInt64(X)
Definition: postgres.h:634
#define Assert(condition)
Definition: c.h:804
#define HASH_PARTITION_SEED
Definition: partition.h:20
static Datum values[MAXATTR]
Definition: bootstrap.c:165
int i
static unsigned hash(unsigned *uv, int n)
Definition: rege_dfa.c:717

◆ 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:3670
void pfree(void *pointer)
Definition: mcxt.c:1057
#define ERROR
Definition: elog.h:45
void * palloc0(Size size)
Definition: mcxt.c:981
uintptr_t Datum
Definition: postgres.h:367
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
#define Assert(condition)
Definition: c.h:804
#define Int32GetDatum(X)
Definition: postgres.h:479
void * palloc(Size size)
Definition: mcxt.c:950
#define elog(elevel,...)
Definition: elog.h:227
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:214
#define NIL
Definition: pg_list.h:65
#define castNode(_type_, nodeptr)
Definition: nodes.h:602
static struct @141 value
#define ERROR
Definition: elog.h:45
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:981
uintptr_t Datum
Definition: postgres.h:367
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:802
void * palloc(Size size)
Definition: mcxt.c:950
#define elog(elevel,...)
Definition: elog.h:227
int i
static int32 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
Definition: partbounds.c:3685
Definition: pg_list.h:50
long val
Definition: informix.c:664
bool constisnull
Definition: primnodes.h:215

◆ create_range_bounds()

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

Definition at line 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:472
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:46
FmgrInfo * partsupfunc
Definition: partcache.h:35
PartitionRangeDatumKind
Definition: parsenodes.h:837
struct cursor * cur
Definition: ecpg.c:28
static int32 qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
Definition: partbounds.c:3702
Datum upper(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:77
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1151
static PartitionRangeBound * make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
Definition: partbounds.c:3320
#define ERROR
Definition: elog.h:45
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:981
uintptr_t Datum
Definition: postgres.h:367
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:803
void * palloc(Size size)
Definition: mcxt.c:950
#define elog(elevel,...)
Definition: elog.h:227
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:1057
#define lfirst_int(lc)
Definition: pg_list.h:170
#define Assert(condition)
Definition: c.h:804
void * palloc(Size size)
Definition: mcxt.c:950
int * old_indexes
Definition: partbounds.c:83
int i
int * merged_indexes
Definition: partbounds.c:79

◆ free_partition_map()

static void free_partition_map ( PartitionMap map)
static

Definition at line 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:1057
bool * merged
Definition: partbounds.c:80
int * old_indexes
Definition: partbounds.c:83
int * merged_indexes
Definition: partbounds.c:79

◆ generate_matching_part_pairs()

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

Definition at line 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:1057
int nparts
Definition: pathnodes.h:750
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:757
void * palloc(Size size)
Definition: mcxt.c:950
int i
int * merged_indexes
Definition: partbounds.c:79

◆ get_hash_partition_greatest_modulus()

int get_hash_partition_greatest_modulus ( PartitionBoundInfo  bound)

Definition at line 3306 of file partbounds.c.

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

3307 {
3308  Assert(bound && bound->strategy == PARTITION_STRATEGY_HASH);
3309  return bound->nindexes;
3310 }
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
#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:45
#define Assert(condition)
Definition: c.h:804
#define elog(elevel,...)
Definition: elog.h:227
#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 3724 of file partbounds.c.

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

Referenced by make_partition_op_expr().

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

◆ get_qual_for_hash()

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

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

3874 {
3876  FuncExpr *fexpr;
3877  Node *relidConst;
3878  Node *modulusConst;
3879  Node *remainderConst;
3880  List *args;
3881  ListCell *partexprs_item;
3882  int i;
3883 
3884  /* Fixed arguments. */
3885  relidConst = (Node *) makeConst(OIDOID,
3886  -1,
3887  InvalidOid,
3888  sizeof(Oid),
3890  false,
3891  true);
3892 
3893  modulusConst = (Node *) makeConst(INT4OID,
3894  -1,
3895  InvalidOid,
3896  sizeof(int32),
3897  Int32GetDatum(spec->modulus),
3898  false,
3899  true);
3900 
3901  remainderConst = (Node *) makeConst(INT4OID,
3902  -1,
3903  InvalidOid,
3904  sizeof(int32),
3905  Int32GetDatum(spec->remainder),
3906  false,
3907  true);
3908 
3909  args = list_make3(relidConst, modulusConst, remainderConst);
3910  partexprs_item = list_head(key->partexprs);
3911 
3912  /* Add an argument for each key column. */
3913  for (i = 0; i < key->partnatts; i++)
3914  {
3915  Node *keyCol;
3916 
3917  /* Left operand */
3918  if (key->partattrs[i] != 0)
3919  {
3920  keyCol = (Node *) makeVar(1,
3921  key->partattrs[i],
3922  key->parttypid[i],
3923  key->parttypmod[i],
3924  key->parttypcoll[i],
3925  0);
3926  }
3927  else
3928  {
3929  keyCol = (Node *) copyObject(lfirst(partexprs_item));
3930  partexprs_item = lnext(key->partexprs, partexprs_item);
3931  }
3932 
3933  args = lappend(args, keyCol);
3934  }
3935 
3936  fexpr = makeFuncExpr(F_SATISFIES_HASH_PARTITION,
3937  BOOLOID,
3938  args,
3939  InvalidOid,
3940  InvalidOid,
3942 
3943  return list_make1(fexpr);
3944 }
#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:533
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:507
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
List * lappend(List *list, void *datum)
Definition: list.c: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:479
int i
#define copyObject(obj)
Definition: nodes.h:649
Definition: pg_list.h:50
#define RelationGetRelid(relation)
Definition: rel.h:457
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 3956 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().

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

◆ get_qual_for_range()

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

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

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

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

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

◆ get_range_nulltest()

static List * get_range_nulltest ( PartitionKey  key)
static

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

4572 {
4573  List *result = NIL;
4574  NullTest *nulltest;
4575  ListCell *partexprs_item;
4576  int i;
4577 
4578  partexprs_item = list_head(key->partexprs);
4579  for (i = 0; i < key->partnatts; i++)
4580  {
4581  Expr *keyCol;
4582 
4583  if (key->partattrs[i] != 0)
4584  {
4585  keyCol = (Expr *) makeVar(1,
4586  key->partattrs[i],
4587  key->parttypid[i],
4588  key->parttypmod[i],
4589  key->parttypcoll[i],
4590  0);
4591  }
4592  else
4593  {
4594  if (partexprs_item == NULL)
4595  elog(ERROR, "wrong number of partition key expressions");
4596  keyCol = copyObject(lfirst(partexprs_item));
4597  partexprs_item = lnext(key->partexprs, partexprs_item);
4598  }
4599 
4600  nulltest = makeNode(NullTest);
4601  nulltest->arg = keyCol;
4602  nulltest->nulltesttype = IS_NOT_NULL;
4603  nulltest->argisrow = false;
4604  nulltest->location = -1;
4605  result = lappend(result, nulltest);
4606  }
4607 
4608  return result;
4609 }
#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:45
Expr * arg
Definition: primnodes.h:1243
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:1244
int32 * parttypmod
Definition: partcache.h:42
#define makeNode(_type_)
Definition: nodes.h:581
#define lfirst(lc)
Definition: pg_list.h:169
int location
Definition: primnodes.h:1246
#define elog(elevel,...)
Definition: elog.h:227
int i
bool argisrow
Definition: primnodes.h:1245
#define copyObject(obj)
Definition: nodes.h:649
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:803

◆ 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:750
bool * merged
Definition: partbounds.c:80
void * palloc(Size size)
Definition: mcxt.c:950
int * old_indexes
Definition: partbounds.c:83
int i
int * merged_indexes
Definition: partbounds.c:79

◆ is_dummy_partition()

static bool is_dummy_partition ( RelOptInfo rel,
int  part_index 
)
static

Definition at line 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:1438
#define Assert(condition)
Definition: c.h:804
struct RelOptInfo ** part_rels
Definition: pathnodes.h:757

◆ make_one_partition_rbound()

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

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

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

◆ make_partition_op_expr()

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

Definition at line 3760 of file partbounds.c.

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

Referenced by get_qual_for_list(), and get_qual_for_range().

3762 {
3763  Oid operoid;
3764  bool need_relabel = false;
3765  Expr *result = NULL;
3766 
3767  /* Get the correct btree operator for this partitioning column */
3768  operoid = get_partition_operator(key, keynum, strategy, &need_relabel);
3769 
3770  /*
3771  * Chosen operator may be such that the non-Const operand needs to be
3772  * coerced, so apply the same; see the comment in
3773  * get_partition_operator().
3774  */
3775  if (!IsA(arg1, Const) &&
3776  (need_relabel ||
3777  key->partcollation[keynum] != key->parttypcoll[keynum]))
3778  arg1 = (Expr *) makeRelabelType(arg1,
3779  key->partopcintype[keynum],
3780  -1,
3781  key->partcollation[keynum],
3783 
3784  /* Generate the actual expression */
3785  switch (key->strategy)
3786  {
3788  {
3789  List *elems = (List *) arg2;
3790  int nelems = list_length(elems);
3791 
3792  Assert(nelems >= 1);
3793  Assert(keynum == 0);
3794 
3795  if (nelems > 1 &&
3796  !type_is_array(key->parttypid[keynum]))
3797  {
3798  ArrayExpr *arrexpr;
3799  ScalarArrayOpExpr *saopexpr;
3800 
3801  /* Construct an ArrayExpr for the right-hand inputs */
3802  arrexpr = makeNode(ArrayExpr);
3803  arrexpr->array_typeid =
3804  get_array_type(key->parttypid[keynum]);
3805  arrexpr->array_collid = key->parttypcoll[keynum];
3806  arrexpr->element_typeid = key->parttypid[keynum];
3807  arrexpr->elements = elems;
3808  arrexpr->multidims = false;
3809  arrexpr->location = -1;
3810 
3811  /* Build leftop = ANY (rightop) */
3812  saopexpr = makeNode(ScalarArrayOpExpr);
3813  saopexpr->opno = operoid;
3814  saopexpr->opfuncid = get_opcode(operoid);
3815  saopexpr->useOr = true;
3816  saopexpr->inputcollid = key->partcollation[keynum];
3817  saopexpr->args = list_make2(arg1, arrexpr);
3818  saopexpr->location = -1;
3819 
3820  result = (Expr *) saopexpr;
3821  }
3822  else
3823  {
3824  List *elemops = NIL;
3825  ListCell *lc;
3826 
3827  foreach(lc, elems)
3828  {
3829  Expr *elem = lfirst(lc),
3830  *elemop;
3831 
3832  elemop = make_opclause(operoid,
3833  BOOLOID,
3834  false,
3835  arg1, elem,
3836  InvalidOid,
3837  key->partcollation[keynum]);
3838  elemops = lappend(elemops, elemop);
3839  }
3840 
3841  result = nelems > 1 ? makeBoolExpr(OR_EXPR, elemops, -1) : linitial(elemops);
3842  }
3843  break;
3844  }
3845 
3847  result = make_opclause(operoid,
3848  BOOLOID,
3849  false,
3850  arg1, arg2,
3851  InvalidOid,
3852  key->partcollation[keynum]);
3853  break;
3854 
3855  default:
3856  elog(ERROR, "invalid partitioning strategy");
3857  break;
3858  }
3859 
3860  return result;
3861 }
#define list_make2(x1, x2)
Definition: pg_list.h:208
bool multidims
Definition: primnodes.h:1016
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:584
Oid get_array_type(Oid typid)
Definition: lsyscache.c:2734
Oid array_typeid
Definition: primnodes.h:1012
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:45
RelabelType * makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod, Oid rcollid, CoercionForm rformat)
Definition: makefuncs.c:402
List * elements
Definition: primnodes.h:1015
List * lappend(List *list, void *datum)
Definition: list.c:336
Oid * partcollation
Definition: partcache.h:38
int location
Definition: primnodes.h:1017
static Oid get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel)
Definition: partbounds.c:3724
#define InvalidOid
Definition: postgres_ext.h:36
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1256
#define makeNode(_type_)
Definition: nodes.h:581
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
Oid array_collid
Definition: primnodes.h:1013
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:802
Oid element_typeid
Definition: primnodes.h:1014
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
Oid * partopcintype
Definition: partcache.h:34
#define elog(elevel,...)
Definition: elog.h:227
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:749
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:472
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:749
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1151
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:367
static void cleanup(void)
Definition: bootstrap.c:862
struct PartitionBoundInfoData * boundinfo
Definition: pathnodes.h:753
#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:802
static int process_outer_partition(PartitionMap *outer_map, PartitionMap *inner_map, bool outer_has_default, bool inner_has_default, int outer_index, int inner_default, JoinType jointype, int *next_index, int *default_index)
Definition: partbounds.c: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:749
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:749
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:862
struct PartitionBoundInfoData * boundinfo
Definition: pathnodes.h:753
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:803
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:837
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:367
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
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:802
void * palloc(Size size)
Definition: mcxt.c:950
int i
struct PartitionBoundInfoData * PartitionBoundInfo
Definition: partdefs.h:16

◆ partition_bounds_create()

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

Definition at line 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:45
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:801
#define Assert(condition)
Definition: c.h:804
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
static PartitionBoundInfo create_range_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
Definition: partbounds.c:598
void * palloc(Size size)
Definition: mcxt.c:950
#define elog(elevel,...)
Definition: elog.h:227
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:801
#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,
List **  outer_parts,
List **  inner_parts 
)