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/namespace.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 "utils/array.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 *partcollation, 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_index, int inner_index, 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 parent, PartitionBoundSpec *spec)
 
PartitionBoundInfo partition_bounds_create (PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
 
static int get_non_null_list_datum_count (PartitionBoundSpec **boundspecs, int nparts)
 
bool partition_bounds_equal (int partnatts, int16 *parttyplen, bool *parttypbyval, PartitionBoundInfo b1, PartitionBoundInfo b2)
 
PartitionBoundInfo partition_bounds_copy (PartitionBoundInfo src, PartitionKey key)
 
PartitionBoundInfo partition_bounds_merge (int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinType jointype, List **outer_parts, List **inner_parts)
 
bool partitions_are_ordered (PartitionBoundInfo boundinfo, Bitmapset *live_parts)
 
void check_new_partition_bound (char *relname, Relation parent, PartitionBoundSpec *spec, ParseState *pstate)
 
void check_default_partition_contents (Relation parent, Relation default_rel, PartitionBoundSpec *new_spec)
 
int get_hash_partition_greatest_modulus (PartitionBoundInfo bound)
 
int32 partition_rbound_datum_cmp (FmgrInfo *partsupfunc, Oid *partcollation, const Datum *rb_datums, PartitionRangeDatumKind *rb_kind, const Datum *tuple_datums, int n_tuple_datums)
 
int partition_list_bsearch (FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
 
int partition_range_datum_bsearch (FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, int nvalues, const Datum *values, bool *is_equal)
 
int partition_hash_bsearch (PartitionBoundInfo boundinfo, int modulus, int remainder)
 
uint64 compute_partition_hash_value (int partnatts, FmgrInfo *partsupfunc, const Oid *partcollation, const Datum *values, const bool *isnull)
 
Datum satisfies_hash_partition (PG_FUNCTION_ARGS)
 
static void check_two_partitions_bounds_range (Relation parent, RangeVar *first_name, PartitionBoundSpec *first_bound, RangeVar *second_name, PartitionBoundSpec *second_bound, bool defaultPart, bool is_merge, ParseState *pstate)
 
static PartitionBoundSpecget_partition_bound_spec (Oid partOid)
 
void calculate_partition_bound_for_merge (Relation parent, List *partNames, List *partOids, PartitionBoundSpec *spec, ParseState *pstate)
 
static Listpartitions_listdatum_intersection (FmgrInfo *partsupfunc, Oid *partcollation, const List *list1, const List *list2)
 
static void check_partitions_not_overlap_list (Relation parent, SinglePartitionSpec **parts, int nparts, ParseState *pstate)
 
static void check_partition_bounds_for_split_range (Relation parent, char *relname, PartitionBoundSpec *spec, Oid splitPartOid, bool first, bool last, bool defaultPart, ParseState *pstate)
 
static void check_partition_bounds_for_split_list (Relation parent, char *relname, PartitionBoundSpec *spec, Oid splitPartOid, ParseState *pstate)
 
static bool find_value_in_new_partitions_list (FmgrInfo *partsupfunc, Oid *partcollation, SinglePartitionSpec **parts, int nparts, Datum value, bool isnull)
 
static void check_parent_values_in_new_partitions (Relation parent, Oid partOid, SinglePartitionSpec **parts, int nparts, ParseState *pstate)
 
void check_partitions_for_split (Relation parent, Oid splitPartOid, List *partlist, ParseState *pstate)
 

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

Definition at line 89 of file partbounds.c.

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

2776{
2777 int cmpval;
2778
2779 if (!*merged_datums)
2780 {
2781 /* First merged partition */
2782 Assert(!*merged_kinds);
2783 Assert(!*merged_indexes);
2784 cmpval = 1;
2785 }
2786 else
2787 {
2788 PartitionRangeBound prev_ub;
2789
2790 Assert(*merged_datums);
2791 Assert(*merged_kinds);
2792 Assert(*merged_indexes);
2793
2794 /* Get the last upper bound. */
2795 prev_ub.index = llast_int(*merged_indexes);
2796 prev_ub.datums = (Datum *) llast(*merged_datums);
2797 prev_ub.kind = (PartitionRangeDatumKind *) llast(*merged_kinds);
2798 prev_ub.lower = false;
2799
2800 /*
2801 * We pass lower1 = false to partition_rbound_cmp() to prevent it from
2802 * considering the last upper bound to be smaller than the lower bound
2803 * of the merged partition when the values of the two range bounds
2804 * compare equal.
2805 */
2806 cmpval = partition_rbound_cmp(partnatts, partsupfuncs, partcollations,
2807 merged_lb->datums, merged_lb->kind,
2808 false, &prev_ub);
2809 Assert(cmpval >= 0);
2810 }
2811
2812 /*
2813 * If the lower bound is higher than the last upper bound, add the lower
2814 * bound with the index as -1 indicating that that is a lower bound; else,
2815 * the last upper bound will be reused as the lower bound of the merged
2816 * partition, so skip this.
2817 */
2818 if (cmpval > 0)
2819 {
2820 *merged_datums = lappend(*merged_datums, merged_lb->datums);
2821 *merged_kinds = lappend(*merged_kinds, merged_lb->kind);
2822 *merged_indexes = lappend_int(*merged_indexes, -1);
2823 }
2824
2825 /* Add the upper bound and index of the merged partition. */
2826 *merged_datums = lappend(*merged_datums, merged_ub->datums);
2827 *merged_kinds = lappend(*merged_kinds, merged_ub->kind);
2828 *merged_indexes = lappend_int(*merged_indexes, merged_index);
2829}
Assert(PointerIsAligned(start, uint64))
List * lappend(List *list, void *datum)
Definition: list.c:339
List * lappend_int(List *list, int datum)
Definition: list.c:357
PartitionRangeDatumKind
Definition: parsenodes.h:951
#define llast_int(l)
Definition: pg_list.h:199
#define llast(l)
Definition: pg_list.h:198
uint64_t Datum
Definition: postgres.h:70
PartitionRangeDatumKind * kind
Definition: partbounds.c:69

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

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

2515{
2516 PartitionBoundInfo merged_bounds;
2517 int ndatums = list_length(merged_datums);
2518 int pos;
2519 ListCell *lc;
2520
2521 merged_bounds = palloc_object(PartitionBoundInfoData);
2522 merged_bounds->strategy = strategy;
2523 merged_bounds->ndatums = ndatums;
2524
2525 merged_bounds->datums = palloc_array(Datum *, ndatums);
2526 pos = 0;
2527 foreach(lc, merged_datums)
2528 merged_bounds->datums[pos++] = (Datum *) lfirst(lc);
2529
2530 if (strategy == PARTITION_STRATEGY_RANGE)
2531 {
2532 Assert(list_length(merged_kinds) == ndatums);
2533 merged_bounds->kind = palloc_array(PartitionRangeDatumKind *, ndatums);
2534 pos = 0;
2535 foreach(lc, merged_kinds)
2536 merged_bounds->kind[pos++] = (PartitionRangeDatumKind *) lfirst(lc);
2537
2538 /* There are ndatums+1 indexes in the case of range partitioning. */
2539 merged_indexes = lappend_int(merged_indexes, -1);
2540 ndatums++;
2541 }
2542 else
2543 {
2544 Assert(strategy == PARTITION_STRATEGY_LIST);
2545 Assert(merged_kinds == NIL);
2546 merged_bounds->kind = NULL;
2547 }
2548
2549 /* interleaved_parts is always NULL for join relations. */
2550 merged_bounds->interleaved_parts = NULL;
2551
2552 Assert(list_length(merged_indexes) == ndatums);
2553 merged_bounds->nindexes = ndatums;
2554 merged_bounds->indexes = palloc_array(int, ndatums);
2555 pos = 0;
2556 foreach(lc, merged_indexes)
2557 merged_bounds->indexes[pos++] = lfirst_int(lc);
2558
2559 merged_bounds->null_index = null_index;
2560 merged_bounds->default_index = default_index;
2561
2562 return merged_bounds;
2563}
#define palloc_object(type)
Definition: fe_memutils.h:74
#define palloc_array(type, count)
Definition: fe_memutils.h:76
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:900
@ PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:901
#define lfirst(lc)
Definition: pg_list.h:172
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define lfirst_int(lc)
Definition: pg_list.h:173
PartitionRangeDatumKind ** kind
Definition: partbounds.h:84
PartitionStrategy strategy
Definition: partbounds.h:81
Bitmapset * interleaved_parts
Definition: partbounds.h:87

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

Referenced by merge_list_bounds(), and merge_range_bounds().

◆ calculate_partition_bound_for_merge()

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

Definition at line 5098 of file partbounds.c.

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

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

Referenced by transformPartitionCmdForMerge().

◆ check_default_partition_contents()

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

Definition at line 3244 of file partbounds.c.

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

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

Referenced by DefineRelation().

◆ check_new_partition_bound()

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

Definition at line 2889 of file partbounds.c.

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

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

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

◆ check_parent_values_in_new_partitions()

static void check_parent_values_in_new_partitions ( Relation  parent,
Oid  partOid,
SinglePartitionSpec **  parts,
int  nparts,
ParseState pstate 
)
static

Definition at line 5622 of file partbounds.c.

5627{
5629 PartitionDesc partdesc = RelationGetPartitionDesc(parent, false);
5630 PartitionBoundInfo boundinfo = partdesc->boundinfo;
5631 int i;
5632 bool found = true;
5633 Datum datum = PointerGetDatum(NULL);
5634
5635 Assert(key->strategy == PARTITION_STRATEGY_LIST);
5636
5637 /*
5638 * Special processing for NULL value. Search for a NULL value if the split
5639 * partition (partOid) contains it.
5640 */
5641 if (partition_bound_accepts_nulls(boundinfo) &&
5642 partdesc->oids[boundinfo->null_index] == partOid)
5643 {
5644 if (!find_value_in_new_partitions_list(&key->partsupfunc[0],
5645 key->partcollation, parts, nparts, datum, true))
5646 found = false;
5647 }
5648
5649 if (!found)
5650 ereport(ERROR,
5651 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5652 errmsg("new partitions combined partition bounds do not contain value (%s) but split partition \"%s\" does",
5653 "NULL",
5654 get_rel_name(partOid)),
5655 errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5656 "ALTER TABLE ... SPLIT PARTITION"));
5657
5658 /*
5659 * Search all values of split partition with partOid in the PartitionDesc
5660 * of partitioned table.
5661 */
5662 for (i = 0; i < boundinfo->ndatums; i++)
5663 {
5664 if (partdesc->oids[boundinfo->indexes[i]] == partOid)
5665 {
5666 /* We found the value that the split partition contains. */
5667 datum = boundinfo->datums[i][0];
5668 if (!find_value_in_new_partitions_list(&key->partsupfunc[0],
5669 key->partcollation, parts, nparts, datum, false))
5670 {
5671 found = false;
5672 break;
5673 }
5674 }
5675 }
5676
5677 if (!found)
5678 {
5679 Const *notFoundVal;
5680
5681 /*
5682 * Make a Const for getting the string representation of the missing
5683 * value.
5684 */
5685 notFoundVal = makeConst(key->parttypid[0],
5686 key->parttypmod[0],
5687 key->parttypcoll[0],
5688 key->parttyplen[0],
5689 datum,
5690 false, /* isnull */
5691 key->parttypbyval[0]);
5692
5693 ereport(ERROR,
5694 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5695 errmsg("new partitions combined partition bounds do not contain value (%s) but split partition \"%s\" does",
5696 deparse_expression((Node *) notFoundVal, NIL, false, false),
5697 get_rel_name(partOid)),
5698 errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5699 "ALTER TABLE ... SPLIT PARTITION"));
5700 }
5701}
int errhint(const char *fmt,...)
Definition: elog.c:1330
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:350
static bool find_value_in_new_partitions_list(FmgrInfo *partsupfunc, Oid *partcollation, SinglePartitionSpec **parts, int nparts, Datum value, bool isnull)
Definition: partbounds.c:5578
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:332
char * deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit)
Definition: ruleutils.c:3648
Definition: nodes.h:135

References Assert(), PartitionDescData::boundinfo, PartitionBoundInfoData::datums, deparse_expression(), ereport, errcode(), errhint(), errmsg(), ERROR, find_value_in_new_partitions_list(), get_rel_name(), i, PartitionBoundInfoData::indexes, sort-test::key, makeConst(), PartitionBoundInfoData::ndatums, NIL, PartitionBoundInfoData::null_index, PartitionDescData::oids, partition_bound_accepts_nulls, PARTITION_STRATEGY_LIST, PointerGetDatum(), RelationGetPartitionDesc(), and RelationGetPartitionKey().

Referenced by check_partitions_for_split().

◆ check_partition_bounds_for_split_list()

static void check_partition_bounds_for_split_list ( Relation  parent,
char *  relname,
PartitionBoundSpec spec,
Oid  splitPartOid,
ParseState pstate 
)
static

Definition at line 5481 of file partbounds.c.

5485{
5487 PartitionDesc partdesc = RelationGetPartitionDesc(parent, false);
5488 PartitionBoundInfo boundinfo = partdesc->boundinfo;
5489 int with = -1;
5490 bool overlap = false;
5491 int overlap_location = -1;
5492
5493 Assert(key->strategy == PARTITION_STRATEGY_LIST);
5495 Assert(boundinfo && boundinfo->strategy == PARTITION_STRATEGY_LIST);
5496
5497 /*
5498 * Search each value of the new partition "spec" in the existing
5499 * partitions. All of them should be in the split partition (with Oid
5500 * splitPartOid).
5501 */
5503 {
5504 overlap_location = exprLocation((Node *) val);
5505 if (!val->constisnull)
5506 {
5507 int offset;
5508 bool equal;
5509
5510 offset = partition_list_bsearch(&key->partsupfunc[0],
5511 key->partcollation,
5512 boundinfo,
5513 val->constvalue,
5514 &equal);
5515 if (offset >= 0 && equal)
5516 {
5517 with = boundinfo->indexes[offset];
5518 if (partdesc->oids[with] != splitPartOid)
5519 {
5520 overlap = true;
5521 break;
5522 }
5523 }
5524 else
5525 ereport(ERROR,
5526 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5527 errmsg("new partition \"%s\" cannot have this value because split partition \"%s\" does not have",
5528 relname,
5529 get_rel_name(splitPartOid)),
5530 parser_errposition(pstate, overlap_location));
5531 }
5532 else if (partition_bound_accepts_nulls(boundinfo))
5533 {
5534 with = boundinfo->null_index;
5535 if (partdesc->oids[with] != splitPartOid)
5536 {
5537 overlap = true;
5538 break;
5539 }
5540 }
5541 else
5542 ereport(ERROR,
5543 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5544 errmsg("new partition \"%s\" cannot have NULL value because split partition \"%s\" does not have",
5545 relname,
5546 get_rel_name(splitPartOid)),
5547 parser_errposition(pstate, overlap_location));
5548 }
5549
5550 if (overlap)
5551 {
5552 Assert(with >= 0);
5553 ereport(ERROR,
5554 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5555 errmsg("new partition \"%s\" would overlap with another (not split) partition \"%s\"",
5556 relname, get_rel_name(partdesc->oids[with])),
5557 parser_errposition(pstate, overlap_location));
5558 }
5559}
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1384
#define foreach_node(type, var, lst)
Definition: pg_list.h:496

References Assert(), PartitionDescData::boundinfo, equal(), ereport, errcode(), errmsg(), ERROR, exprLocation(), foreach_node, get_rel_name(), PartitionBoundInfoData::indexes, sort-test::key, PartitionBoundSpec::listdatums, PartitionBoundInfoData::null_index, PartitionDescData::oids, parser_errposition(), partition_bound_accepts_nulls, partition_list_bsearch(), PARTITION_STRATEGY_LIST, RelationGetPartitionDesc(), RelationGetPartitionKey(), relname, PartitionBoundSpec::strategy, PartitionBoundInfoData::strategy, and val.

Referenced by check_partitions_for_split().

◆ check_partition_bounds_for_split_range()

static void check_partition_bounds_for_split_range ( Relation  parent,
char *  relname,
PartitionBoundSpec spec,
Oid  splitPartOid,
bool  first,
bool  last,
bool  defaultPart,
ParseState pstate 
)
static

Definition at line 5324 of file partbounds.c.

5332{
5335 *upper;
5336 int cmpval;
5337
5338 Assert(key->strategy == PARTITION_STRATEGY_RANGE);
5340
5341 lower = make_one_partition_rbound(key, -1, spec->lowerdatums, true);
5342 upper = make_one_partition_rbound(key, -1, spec->upperdatums, false);
5343
5344 /*
5345 * First, check if the resulting range would be empty with the specified
5346 * lower and upper bounds. partition_rbound_cmp cannot return zero here,
5347 * since the lower-bound flags are different.
5348 */
5349 cmpval = partition_rbound_cmp(key->partnatts,
5350 key->partsupfunc,
5351 key->partcollation,
5352 lower->datums, lower->kind,
5353 true, upper);
5354 Assert(cmpval != 0);
5355 if (cmpval > 0)
5356 {
5357 /* Point to the problematic key in the lower datums list. */
5358 PartitionRangeDatum *datum = list_nth(spec->lowerdatums, cmpval - 1);
5359
5360 ereport(ERROR,
5361 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5362 errmsg("empty range bound specified for partition \"%s\"",
5363 relname),
5364 errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
5367 parser_errposition(pstate, exprLocation((Node *) datum)));
5368 }
5369
5370 /*
5371 * Need to check first and last partitions (from the set of new
5372 * partitions)
5373 */
5374 if (first || last)
5375 {
5376 PartitionBoundSpec *split_spec = get_partition_bound_spec(splitPartOid);
5377 PartitionRangeDatum *datum;
5378
5379 if (first)
5380 {
5381 PartitionRangeBound *split_lower;
5382
5383 split_lower = make_one_partition_rbound(key, -1, split_spec->lowerdatums, true);
5384
5385 cmpval = partition_rbound_cmp(key->partnatts,
5386 key->partsupfunc,
5387 key->partcollation,
5388 lower->datums, lower->kind,
5389 true, split_lower);
5390 if (cmpval != 0)
5391 datum = list_nth(spec->lowerdatums, abs(cmpval) - 1);
5392
5393 /*
5394 * The lower bound of "spec" must equal the lower bound of the
5395 * split partition. However, if one of the new partitions is
5396 * DEFAULT, then it is ok for the new partition's lower bound to
5397 * be greater than that of the split partition.
5398 */
5399 if (!defaultPart)
5400 {
5401 if (cmpval != 0)
5402 ereport(ERROR,
5403 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5404 errmsg("lower bound of partition \"%s\" is not equal to lower bound of split partition \"%s\"",
5405 relname,
5406 get_rel_name(splitPartOid)),
5407 errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5408 "ALTER TABLE ... SPLIT PARTITION"),
5409 parser_errposition(pstate, exprLocation((Node *) datum)));
5410 }
5411 else if (cmpval < 0)
5412 ereport(ERROR,
5413 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5414 errmsg("lower bound of partition \"%s\" is less than lower bound of split partition \"%s\"",
5415 relname,
5416 get_rel_name(splitPartOid)),
5417 errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5418 "ALTER TABLE ... SPLIT PARTITION"),
5419 parser_errposition(pstate, exprLocation((Node *) datum)));
5420 }
5421 else
5422 {
5423 PartitionRangeBound *split_upper;
5424
5425 split_upper = make_one_partition_rbound(key, -1, split_spec->upperdatums, false);
5426
5427 cmpval = partition_rbound_cmp(key->partnatts,
5428 key->partsupfunc,
5429 key->partcollation,
5430 upper->datums, upper->kind,
5431 false, split_upper);
5432 if (cmpval != 0)
5433 datum = list_nth(spec->upperdatums, abs(cmpval) - 1);
5434
5435 /*
5436 * The upper bound of "spec" must equal the upper bound of the
5437 * split partition. However, if one of the new partitions is
5438 * DEFAULT, then it is ok for the new partition's upper bound to
5439 * be less than that of the split partition.
5440 */
5441 if (!defaultPart)
5442 {
5443 if (cmpval != 0)
5444 ereport(ERROR,
5445 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5446 errmsg("upper bound of partition \"%s\" is not equal to upper bound of split partition \"%s\"",
5447 relname,
5448 get_rel_name(splitPartOid)),
5449 errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5450 "ALTER TABLE ... SPLIT PARTITION"),
5451 parser_errposition(pstate, exprLocation((Node *) datum)));
5452 }
5453 else if (cmpval > 0)
5454 ereport(ERROR,
5455 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5456 errmsg("upper bound of partition \"%s\" is greater than upper bound of split partition \"%s\"",
5457 relname,
5458 get_rel_name(splitPartOid)),
5459 errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5460 "ALTER TABLE ... SPLIT PARTITION"),
5461 parser_errposition(pstate, exprLocation((Node *) datum)));
5462 }
5463 }
5464}

References Assert(), ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, exprLocation(), get_partition_bound_spec(), get_range_partbound_string(), get_rel_name(), sort-test::key, list_nth(), lower(), PartitionBoundSpec::lowerdatums, make_one_partition_rbound(), parser_errposition(), partition_rbound_cmp(), PARTITION_STRATEGY_RANGE, RelationGetPartitionKey(), relname, PartitionBoundSpec::strategy, upper(), and PartitionBoundSpec::upperdatums.

Referenced by check_partitions_for_split().

◆ check_partitions_for_split()

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

Definition at line 5723 of file partbounds.c.

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

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

Referenced by transformPartitionCmdForSplit().

◆ check_partitions_not_overlap_list()

static void check_partitions_not_overlap_list ( Relation  parent,
SinglePartitionSpec **  parts,
int  nparts,
ParseState pstate 
)
static

Definition at line 5258 of file partbounds.c.

5262{
5264 int i,
5265 j;
5266 SinglePartitionSpec *sps1,
5267 *sps2;
5268 List *overlap;
5269
5270 Assert(key->strategy == PARTITION_STRATEGY_LIST);
5271
5272 for (i = 0; i < nparts; i++)
5273 {
5274 sps1 = parts[i];
5275
5276 for (j = i + 1; j < nparts; j++)
5277 {
5278 sps2 = parts[j];
5279
5280 overlap = partitions_listdatum_intersection(&key->partsupfunc[0],
5281 key->partcollation,
5282 sps1->bound->listdatums,
5283 sps2->bound->listdatums);
5284 if (list_length(overlap) > 0)
5285 {
5286 Const *val = (Const *) linitial_node(Const, overlap);
5287
5288 ereport(ERROR,
5289 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5290 errmsg("new partition \"%s\" would overlap with another new partition \"%s\"",
5291 sps1->name->relname, sps2->name->relname),
5292 parser_errposition(pstate, exprLocation((Node *) val)));
5293 }
5294 }
5295 }
5296}
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:229
int j
Definition: isn.c:78
static List * partitions_listdatum_intersection(FmgrInfo *partsupfunc, Oid *partcollation, const List *list1, const List *list2)
Definition: partbounds.c:5204
#define linitial_node(type, l)
Definition: pg_list.h:181

References Assert(), SinglePartitionSpec::bound, ereport, errcode(), errmsg(), ERROR, exprLocation(), i, j, sort-test::key, linitial_node, list_length(), PartitionBoundSpec::listdatums, SinglePartitionSpec::name, parser_errposition(), PARTITION_STRATEGY_LIST, partitions_listdatum_intersection(), PG_USED_FOR_ASSERTS_ONLY, RelationGetPartitionKey(), RangeVar::relname, and val.

Referenced by check_partitions_for_split().

◆ check_two_partitions_bounds_range()

static void check_two_partitions_bounds_range ( Relation  parent,
RangeVar first_name,
PartitionBoundSpec first_bound,
RangeVar second_name,
PartitionBoundSpec second_bound,
bool  defaultPart,
bool  is_merge,
ParseState pstate 
)
static

Definition at line 4997 of file partbounds.c.

5005{
5007 PartitionRangeBound *first_upper;
5008 PartitionRangeBound *second_lower;
5009 int cmpval;
5010
5011 Assert(key->strategy == PARTITION_STRATEGY_RANGE);
5012
5013 first_upper = make_one_partition_rbound(key, -1, first_bound->upperdatums, false);
5014 second_lower = make_one_partition_rbound(key, -1, second_bound->lowerdatums, true);
5015
5016 /*
5017 * lower1 argument of partition_rbound_cmp() is set to false for the
5018 * correct comparison result of the lower and upper bounds.
5019 */
5020 cmpval = partition_rbound_cmp(key->partnatts,
5021 key->partsupfunc,
5022 key->partcollation,
5023 second_lower->datums, second_lower->kind,
5024 false, first_upper);
5025 if ((!defaultPart && cmpval) || (defaultPart && cmpval < 0))
5026 {
5027 PartitionRangeDatum *datum = linitial(second_bound->lowerdatums);
5028
5029 if (is_merge)
5030 ereport(ERROR,
5031 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5032 errmsg("can not merge partition \"%s\" together with partition \"%s\"",
5033 second_name->relname, first_name->relname),
5034 errdetail("lower bound of partition \"%s\" is not equal to the upper bound of partition \"%s\"",
5035 second_name->relname, first_name->relname),
5036 errhint("ALTER TABLE ... MERGE PARTITIONS requires the partition bounds to be adjacent."),
5037 parser_errposition(pstate, datum->location));
5038 else
5039 ereport(ERROR,
5040 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5041 errmsg("can not split to partition \"%s\" together with partition \"%s\"",
5042 second_name->relname, first_name->relname),
5043 errdetail("lower bound of partition \"%s\" is not equal to the upper bound of partition \"%s\"",
5044 second_name->relname, first_name->relname),
5045 errhint("ALTER TABLE ... SPLIT PARTITION requires the partition bounds to be adjacent."),
5046 parser_errposition(pstate, datum->location));
5047 }
5048}

References Assert(), PartitionRangeBound::datums, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, sort-test::key, PartitionRangeBound::kind, linitial, PartitionRangeDatum::location, PartitionBoundSpec::lowerdatums, make_one_partition_rbound(), parser_errposition(), partition_rbound_cmp(), PARTITION_STRATEGY_RANGE, RelationGetPartitionKey(), RangeVar::relname, and PartitionBoundSpec::upperdatums.

Referenced by calculate_partition_bound_for_merge(), and check_partitions_for_split().

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

2662{
2663 /*
2664 * Check if the outer partition's upper bound is lower than the inner
2665 * partition's lower bound; if so the partitions aren't overlapping.
2666 */
2667 if (compare_range_bounds(partnatts, partsupfuncs, partcollations,
2668 outer_ub, inner_lb) < 0)
2669 {
2670 *lb_cmpval = -1;
2671 *ub_cmpval = -1;
2672 return false;
2673 }
2674
2675 /*
2676 * Check if the outer partition's lower bound is higher than the inner
2677 * partition's upper bound; if so the partitions aren't overlapping.
2678 */
2679 if (compare_range_bounds(partnatts, partsupfuncs, partcollations,
2680 outer_lb, inner_ub) > 0)
2681 {
2682 *lb_cmpval = 1;
2683 *ub_cmpval = 1;
2684 return false;
2685 }
2686
2687 /* All other cases indicate overlapping partitions. */
2688 *lb_cmpval = compare_range_bounds(partnatts, partsupfuncs, partcollations,
2689 outer_lb, inner_lb);
2690 *ub_cmpval = compare_range_bounds(partnatts, partsupfuncs, partcollations,
2691 outer_ub, inner_ub);
2692 return true;
2693}
#define compare_range_bounds(partnatts, partsupfunc, partcollations, bound1, bound2)
Definition: partbounds.c:89

References compare_range_bounds.

Referenced by merge_range_bounds().

◆ compute_partition_hash_value()

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

Definition at line 4714 of file partbounds.c.

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

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

Referenced by get_matching_hash_bounds(), and get_partition_for_tuple().

◆ create_hash_bounds()

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

Definition at line 348 of file partbounds.c.

350{
351 PartitionBoundInfo boundinfo;
352 PartitionHashBound *hbounds;
353 int i;
354 int greatest_modulus;
355 Datum *boundDatums;
356
358 boundinfo->strategy = key->strategy;
359 /* No special hash partitions. */
360 boundinfo->null_index = -1;
361 boundinfo->default_index = -1;
362
363 hbounds = palloc_array(PartitionHashBound, nparts);
364
365 /* Convert from node to the internal representation */
366 for (i = 0; i < nparts; i++)
367 {
368 PartitionBoundSpec *spec = boundspecs[i];
369
371 elog(ERROR, "invalid strategy in partition bound spec");
372
373 hbounds[i].modulus = spec->modulus;
374 hbounds[i].remainder = spec->remainder;
375 hbounds[i].index = i;
376 }
377
378 /* Sort all the bounds in ascending order */
379 qsort(hbounds, nparts, sizeof(PartitionHashBound),
381
382 /* After sorting, moduli are now stored in ascending order. */
383 greatest_modulus = hbounds[nparts - 1].modulus;
384
385 boundinfo->ndatums = nparts;
386 boundinfo->datums = palloc0_array(Datum *, nparts);
387 boundinfo->kind = NULL;
388 boundinfo->interleaved_parts = NULL;
389 boundinfo->nindexes = greatest_modulus;
390 boundinfo->indexes = (int *) palloc(greatest_modulus * sizeof(int));
391 for (i = 0; i < greatest_modulus; i++)
392 boundinfo->indexes[i] = -1;
393
394 /*
395 * In the loop below, to save from allocating a series of small datum
396 * arrays, here we just allocate a single array and below we'll just
397 * assign a portion of this array per partition.
398 */
399 boundDatums = (Datum *) palloc(nparts * 2 * sizeof(Datum));
400
401 /*
402 * For hash partitioning, there are as many datums (modulus and remainder
403 * pairs) as there are partitions. Indexes are simply values ranging from
404 * 0 to (nparts - 1).
405 */
406 for (i = 0; i < nparts; i++)
407 {
408 int modulus = hbounds[i].modulus;
409 int remainder = hbounds[i].remainder;
410
411 boundinfo->datums[i] = &boundDatums[i * 2];
412 boundinfo->datums[i][0] = Int32GetDatum(modulus);
413 boundinfo->datums[i][1] = Int32GetDatum(remainder);
414
415 while (remainder < greatest_modulus)
416 {
417 /* overlap? */
418 Assert(boundinfo->indexes[remainder] == -1);
419 boundinfo->indexes[remainder] = i;
420 remainder += modulus;
421 }
422
423 (*mapping)[hbounds[i].index] = i;
424 }
425 pfree(hbounds);
426
427 return boundinfo;
428}
#define palloc0_object(type)
Definition: fe_memutils.h:75
void * palloc(Size size)
Definition: mcxt.c:1365
static int32 qsort_partition_hbound_cmp(const void *a, const void *b)
Definition: partbounds.c:3770
#define qsort(a, b, c, d)
Definition: port.h:499
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:222

References Assert(), PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, elog, ERROR, i, PartitionHashBound::index, PartitionBoundInfoData::indexes, Int32GetDatum(), PartitionBoundInfoData::interleaved_parts, sort-test::key, PartitionBoundInfoData::kind, PartitionHashBound::modulus, PartitionBoundSpec::modulus, PartitionBoundInfoData::ndatums, PartitionBoundInfoData::nindexes, PartitionBoundInfoData::null_index, palloc(), palloc0_array, palloc0_object, palloc_array, PARTITION_STRATEGY_HASH, pfree(), qsort, qsort_partition_hbound_cmp(), PartitionHashBound::remainder, remainder, PartitionBoundSpec::remainder, PartitionBoundSpec::strategy, and PartitionBoundInfoData::strategy.

Referenced by partition_bounds_create().

◆ create_list_bounds()

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

Definition at line 461 of file partbounds.c.

463{
464 PartitionBoundInfo boundinfo;
465 PartitionListValue *all_values;
466 int i;
467 int j;
468 int ndatums;
469 int next_index = 0;
470 int default_index = -1;
471 int null_index = -1;
472 Datum *boundDatums;
473
475 boundinfo->strategy = key->strategy;
476 /* Will be set correctly below. */
477 boundinfo->null_index = -1;
478 boundinfo->default_index = -1;
479
480 ndatums = get_non_null_list_datum_count(boundspecs, nparts);
481 all_values = (PartitionListValue *)
482 palloc(ndatums * sizeof(PartitionListValue));
483
484 /* Create a unified list of non-null values across all partitions. */
485 for (j = 0, i = 0; i < nparts; i++)
486 {
487 PartitionBoundSpec *spec = boundspecs[i];
488 ListCell *c;
489
491 elog(ERROR, "invalid strategy in partition bound spec");
492
493 /*
494 * Note the index of the partition bound spec for the default
495 * partition. There's no datum to add to the list on non-null datums
496 * for this partition.
497 */
498 if (spec->is_default)
499 {
500 default_index = i;
501 continue;
502 }
503
504 foreach(c, spec->listdatums)
505 {
507
508 if (!val->constisnull)
509 {
510 all_values[j].index = i;
511 all_values[j].value = val->constvalue;
512 j++;
513 }
514 else
515 {
516 /*
517 * Never put a null into the values array; save the index of
518 * the partition that stores nulls, instead.
519 */
520 if (null_index != -1)
521 elog(ERROR, "found null more than once");
522 null_index = i;
523 }
524 }
525 }
526
527 /* ensure we found a Datum for every slot in the all_values array */
528 Assert(j == ndatums);
529
530 qsort_arg(all_values, ndatums, sizeof(PartitionListValue),
532
533 boundinfo->ndatums = ndatums;
534 boundinfo->datums = palloc0_array(Datum *, ndatums);
535 boundinfo->kind = NULL;
536 boundinfo->interleaved_parts = NULL;
537 boundinfo->nindexes = ndatums;
538 boundinfo->indexes = (int *) palloc(ndatums * sizeof(int));
539
540 /*
541 * In the loop below, to save from allocating a series of small datum
542 * arrays, here we just allocate a single array and below we'll just
543 * assign a portion of this array per datum.
544 */
545 boundDatums = (Datum *) palloc(ndatums * sizeof(Datum));
546
547 /*
548 * Copy values. Canonical indexes are values ranging from 0 to (nparts -
549 * 1) assigned to each partition such that all datums of a given partition
550 * receive the same value. The value for a given partition is the index of
551 * that partition's smallest datum in the all_values[] array.
552 */
553 for (i = 0; i < ndatums; i++)
554 {
555 int orig_index = all_values[i].index;
556
557 boundinfo->datums[i] = &boundDatums[i];
558 boundinfo->datums[i][0] = datumCopy(all_values[i].value,
559 key->parttypbyval[0],
560 key->parttyplen[0]);
561
562 /* If the old index has no mapping, assign one */
563 if ((*mapping)[orig_index] == -1)
564 (*mapping)[orig_index] = next_index++;
565
566 boundinfo->indexes[i] = (*mapping)[orig_index];
567 }
568
569 pfree(all_values);
570
571 /*
572 * Set the canonical value for null_index, if any.
573 *
574 * It is possible that the null-accepting partition has not been assigned
575 * an index yet, which could happen if such partition accepts only null
576 * and hence not handled in the above loop which only looked at non-null
577 * values.
578 */
579 if (null_index != -1)
580 {
581 Assert(null_index >= 0);
582 if ((*mapping)[null_index] == -1)
583 (*mapping)[null_index] = next_index++;
584 boundinfo->null_index = (*mapping)[null_index];
585 }
586
587 /* Set the canonical value for default_index, if any. */
588 if (default_index != -1)
589 {
590 /*
591 * The default partition accepts any value not specified in the lists
592 * of other partitions, hence it should not get mapped index while
593 * assigning those for non-null datums.
594 */
595 Assert(default_index >= 0);
596 Assert((*mapping)[default_index] == -1);
597 (*mapping)[default_index] = next_index++;
598 boundinfo->default_index = (*mapping)[default_index];
599 }
600
601 /*
602 * Calculate interleaved partitions. Here we look for partitions which
603 * might be interleaved with other partitions and set a bit in
604 * interleaved_parts for any partitions which may be interleaved with
605 * another partition.
606 */
607
608 /*
609 * There must be multiple partitions to have any interleaved partitions,
610 * otherwise there's nothing to interleave with.
611 */
612 if (nparts > 1)
613 {
614 /*
615 * Short-circuit check to see if only 1 Datum is allowed per
616 * partition. When this is true there's no need to do the more
617 * expensive checks to look for interleaved values.
618 */
619 if (boundinfo->ndatums +
621 partition_bound_has_default(boundinfo) != nparts)
622 {
623 int last_index = -1;
624
625 /*
626 * Since the indexes array is sorted in Datum order, if any
627 * partitions are interleaved then it will show up by the
628 * partition indexes not being in ascending order. Here we check
629 * for that and record all partitions that are out of order.
630 */
631 for (i = 0; i < boundinfo->nindexes; i++)
632 {
633 int index = boundinfo->indexes[i];
634
635 if (index < last_index)
636 boundinfo->interleaved_parts = bms_add_member(boundinfo->interleaved_parts,
637 index);
638
639 /*
640 * Otherwise, if the null_index exists in the indexes array,
641 * then the NULL partition must also allow some other Datum,
642 * therefore it's "interleaved".
643 */
644 else if (partition_bound_accepts_nulls(boundinfo) &&
645 index == boundinfo->null_index)
646 boundinfo->interleaved_parts = bms_add_member(boundinfo->interleaved_parts,
647 index);
648
649 last_index = index;
650 }
651 }
652
653 /*
654 * The DEFAULT partition is the "catch-all" partition that can contain
655 * anything that does not belong to any other partition. If there are
656 * any other partitions then the DEFAULT partition must be marked as
657 * interleaved.
658 */
659 if (partition_bound_has_default(boundinfo))
660 boundinfo->interleaved_parts = bms_add_member(boundinfo->interleaved_parts,
661 boundinfo->default_index);
662 }
663
664
665 /* All partitions must now have been assigned canonical indexes. */
666 Assert(next_index == nparts);
667 return boundinfo;
668}
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:814
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
static struct @171 value
static int get_non_null_list_datum_count(PartitionBoundSpec **boundspecs, int nparts)
Definition: partbounds.c:435
static int32 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
Definition: partbounds.c:3785
char * c

References Assert(), bms_add_member(), datumCopy(), PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, elog, ERROR, get_non_null_list_datum_count(), i, PartitionListValue::index, PartitionBoundInfoData::indexes, PartitionBoundInfoData::interleaved_parts, PartitionBoundSpec::is_default, j, sort-test::key, PartitionBoundInfoData::kind, lfirst_node, PartitionBoundSpec::listdatums, PartitionBoundInfoData::ndatums, PartitionBoundInfoData::nindexes, PartitionBoundInfoData::null_index, palloc(), palloc0_array, palloc0_object, partition_bound_accepts_nulls, partition_bound_has_default, PARTITION_STRATEGY_LIST, pfree(), qsort_arg(), qsort_partition_list_value_cmp(), PartitionBoundSpec::strategy, PartitionBoundInfoData::strategy, val, PartitionListValue::value, and value.

Referenced by partition_bounds_create().

◆ create_range_bounds()

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

Definition at line 675 of file partbounds.c.

677{
678 PartitionBoundInfo boundinfo;
679 PartitionRangeBound **rbounds = NULL;
680 PartitionRangeBound **all_bounds,
681 *prev;
682 int i,
683 k,
684 partnatts;
685 int ndatums = 0;
686 int default_index = -1;
687 int next_index = 0;
688 Datum *boundDatums;
689 PartitionRangeDatumKind *boundKinds;
690
692 boundinfo->strategy = key->strategy;
693 /* There is no special null-accepting range partition. */
694 boundinfo->null_index = -1;
695 /* Will be set correctly below. */
696 boundinfo->default_index = -1;
697
698 all_bounds = palloc0_array(PartitionRangeBound *, 2 * nparts);
699
700 /* Create a unified list of range bounds across all the partitions. */
701 ndatums = 0;
702 for (i = 0; i < nparts; i++)
703 {
704 PartitionBoundSpec *spec = boundspecs[i];
706 *upper;
707
709 elog(ERROR, "invalid strategy in partition bound spec");
710
711 /*
712 * Note the index of the partition bound spec for the default
713 * partition. There's no datum to add to the all_bounds array for
714 * this partition.
715 */
716 if (spec->is_default)
717 {
718 default_index = i;
719 continue;
720 }
721
724 all_bounds[ndatums++] = lower;
725 all_bounds[ndatums++] = upper;
726 }
727
728 Assert(ndatums == nparts * 2 ||
729 (default_index != -1 && ndatums == (nparts - 1) * 2));
730
731 /* Sort all the bounds in ascending order */
732 qsort_arg(all_bounds, ndatums,
733 sizeof(PartitionRangeBound *),
735 key);
736
737 /* Save distinct bounds from all_bounds into rbounds. */
738 rbounds = (PartitionRangeBound **)
739 palloc(ndatums * sizeof(PartitionRangeBound *));
740 k = 0;
741 prev = NULL;
742 for (i = 0; i < ndatums; i++)
743 {
744 PartitionRangeBound *cur = all_bounds[i];
745 bool is_distinct = false;
746 int j;
747
748 /* Is the current bound distinct from the previous one? */
749 for (j = 0; j < key->partnatts; j++)
750 {
751 Datum cmpval;
752
753 if (prev == NULL || cur->kind[j] != prev->kind[j])
754 {
755 is_distinct = true;
756 break;
757 }
758
759 /*
760 * If the bounds are both MINVALUE or MAXVALUE, stop now and treat
761 * them as equal, since any values after this point must be
762 * ignored.
763 */
764 if (cur->kind[j] != PARTITION_RANGE_DATUM_VALUE)
765 break;
766
767 cmpval = FunctionCall2Coll(&key->partsupfunc[j],
768 key->partcollation[j],
769 cur->datums[j],
770 prev->datums[j]);
771 if (DatumGetInt32(cmpval) != 0)
772 {
773 is_distinct = true;
774 break;
775 }
776 }
777
778 /*
779 * Only if the bound is distinct save it into a temporary array, i.e,
780 * rbounds which is later copied into boundinfo datums array.
781 */
782 if (is_distinct)
783 rbounds[k++] = all_bounds[i];
784
785 prev = cur;
786 }
787
788 pfree(all_bounds);
789
790 /* Update ndatums to hold the count of distinct datums. */
791 ndatums = k;
792
793 /*
794 * Add datums to boundinfo. Canonical indexes are values ranging from 0
795 * to nparts - 1, assigned in that order to each partition's upper bound.
796 * For 'datums' elements that are lower bounds, there is -1 in the
797 * 'indexes' array to signify that no partition exists for the values less
798 * than such a bound and greater than or equal to the previous upper
799 * bound.
800 */
801 boundinfo->ndatums = ndatums;
802 boundinfo->datums = palloc0_array(Datum *, ndatums);
803 boundinfo->kind = palloc0_array(PartitionRangeDatumKind *, ndatums);
804 boundinfo->interleaved_parts = NULL;
805
806 /*
807 * For range partitioning, an additional value of -1 is stored as the last
808 * element of the indexes[] array.
809 */
810 boundinfo->nindexes = ndatums + 1;
811 boundinfo->indexes = palloc_array(int, (ndatums + 1));
812
813 /*
814 * In the loop below, to save from allocating a series of small arrays,
815 * here we just allocate a single array for Datums and another for
816 * PartitionRangeDatumKinds, below we'll just assign a portion of these
817 * arrays in each loop.
818 */
819 partnatts = key->partnatts;
820 boundDatums = (Datum *) palloc(ndatums * partnatts * sizeof(Datum));
821 boundKinds = palloc_array(PartitionRangeDatumKind, ndatums * partnatts);
822
823 for (i = 0; i < ndatums; i++)
824 {
825 int j;
826
827 boundinfo->datums[i] = &boundDatums[i * partnatts];
828 boundinfo->kind[i] = &boundKinds[i * partnatts];
829 for (j = 0; j < partnatts; j++)
830 {
831 if (rbounds[i]->kind[j] == PARTITION_RANGE_DATUM_VALUE)
832 boundinfo->datums[i][j] =
833 datumCopy(rbounds[i]->datums[j],
834 key->parttypbyval[j],
835 key->parttyplen[j]);
836 boundinfo->kind[i][j] = rbounds[i]->kind[j];
837 }
838
839 /*
840 * There is no mapping for invalid indexes.
841 *
842 * Any lower bounds in the rbounds array have invalid indexes
843 * assigned, because the values between the previous bound (if there
844 * is one) and this (lower) bound are not part of the range of any
845 * existing partition.
846 */
847 if (rbounds[i]->lower)
848 boundinfo->indexes[i] = -1;
849 else
850 {
851 int orig_index = rbounds[i]->index;
852
853 /* If the old index has no mapping, assign one */
854 if ((*mapping)[orig_index] == -1)
855 (*mapping)[orig_index] = next_index++;
856
857 boundinfo->indexes[i] = (*mapping)[orig_index];
858 }
859 }
860
861 pfree(rbounds);
862
863 /* Set the canonical value for default_index, if any. */
864 if (default_index != -1)
865 {
866 Assert(default_index >= 0 && (*mapping)[default_index] == -1);
867 (*mapping)[default_index] = next_index++;
868 boundinfo->default_index = (*mapping)[default_index];
869 }
870
871 /* The extra -1 element. */
872 Assert(i == ndatums);
873 boundinfo->indexes[i] = -1;
874
875 /* All partitions must now have been assigned canonical indexes. */
876 Assert(next_index == nparts);
877 return boundinfo;
878}
struct cursor * cur
Definition: ecpg.c:29
@ PARTITION_RANGE_DATUM_VALUE
Definition: parsenodes.h:953

References Assert(), cur, datumCopy(), DatumGetInt32(), PartitionRangeBound::datums, PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, elog, ERROR, FunctionCall2Coll(), i, PartitionRangeBound::index, PartitionBoundInfoData::indexes, PartitionBoundInfoData::interleaved_parts, PartitionBoundSpec::is_default, j, sort-test::key, PartitionRangeBound::kind, PartitionBoundInfoData::kind, lower(), PartitionBoundSpec::lowerdatums, make_one_partition_rbound(), PartitionBoundInfoData::ndatums, PartitionBoundInfoData::nindexes, PartitionBoundInfoData::null_index, palloc(), palloc0_array, palloc0_object, palloc_array, PARTITION_RANGE_DATUM_VALUE, PARTITION_STRATEGY_RANGE, pfree(), qsort_arg(), qsort_partition_rbound_cmp(), PartitionBoundSpec::strategy, PartitionBoundInfoData::strategy, upper(), and PartitionBoundSpec::upperdatums.

Referenced by partition_bounds_create().

◆ find_value_in_new_partitions_list()

static bool find_value_in_new_partitions_list ( FmgrInfo partsupfunc,
Oid partcollation,
SinglePartitionSpec **  parts,
int  nparts,
Datum  value,
bool  isnull 
)
static

Definition at line 5578 of file partbounds.c.

5584{
5585 for (int i = 0; i < nparts; i++)
5586 {
5587 SinglePartitionSpec *sps = parts[i];
5588
5590 {
5591 if (isnull && val->constisnull)
5592 return true;
5593
5594 if (!isnull && !val->constisnull)
5595 {
5596 if (DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
5597 partcollation[0],
5598 val->constvalue,
5599 value)) == 0)
5600 return true;
5601 }
5602 }
5603 }
5604 return false;
5605}

References SinglePartitionSpec::bound, DatumGetInt32(), foreach_node, FunctionCall2Coll(), i, PartitionBoundSpec::listdatums, val, and value.

Referenced by check_parent_values_in_new_partitions().

◆ fix_merged_indexes()

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

Definition at line 2379 of file partbounds.c.

2381{
2382 int *new_indexes;
2383 int merged_index;
2384 int i;
2385 ListCell *lc;
2386
2387 Assert(nmerged > 0);
2388
2389 new_indexes = palloc_array(int, nmerged);
2390 for (i = 0; i < nmerged; i++)
2391 new_indexes[i] = -1;
2392
2393 /* Build the mapping of old merged indexes to new merged indexes. */
2394 if (outer_map->did_remapping)
2395 {
2396 for (i = 0; i < outer_map->nparts; i++)
2397 {
2398 merged_index = outer_map->old_indexes[i];
2399 if (merged_index >= 0)
2400 new_indexes[merged_index] = outer_map->merged_indexes[i];
2401 }
2402 }
2403 if (inner_map->did_remapping)
2404 {
2405 for (i = 0; i < inner_map->nparts; i++)
2406 {
2407 merged_index = inner_map->old_indexes[i];
2408 if (merged_index >= 0)
2409 new_indexes[merged_index] = inner_map->merged_indexes[i];
2410 }
2411 }
2412
2413 /* Fix the merged_indexes list using the mapping. */
2414 foreach(lc, merged_indexes)
2415 {
2416 merged_index = lfirst_int(lc);
2417 Assert(merged_index >= 0);
2418 if (new_indexes[merged_index] >= 0)
2419 lfirst_int(lc) = new_indexes[merged_index];
2420 }
2421
2422 pfree(new_indexes);
2423}
bool did_remapping
Definition: partbounds.c:83
int * old_indexes
Definition: partbounds.c:84
int * merged_indexes
Definition: partbounds.c:80

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

Referenced by merge_list_bounds().

◆ free_partition_map()

static void free_partition_map ( PartitionMap map)
static

Definition at line 1826 of file partbounds.c.

1827{
1828 pfree(map->merged_indexes);
1829 pfree(map->merged);
1830 pfree(map->old_indexes);
1831}
bool * merged
Definition: partbounds.c:81

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

Referenced by merge_list_bounds(), and merge_range_bounds().

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

2437{
2438 int outer_nparts = outer_map->nparts;
2439 int inner_nparts = inner_map->nparts;
2440 int *outer_indexes;
2441 int *inner_indexes;
2442 int max_nparts;
2443 int i;
2444
2445 Assert(nmerged > 0);
2446 Assert(*outer_parts == NIL);
2447 Assert(*inner_parts == NIL);
2448
2449 outer_indexes = palloc_array(int, nmerged);
2450 inner_indexes = palloc_array(int, nmerged);
2451 for (i = 0; i < nmerged; i++)
2452 outer_indexes[i] = inner_indexes[i] = -1;
2453
2454 /* Set pairs of matching partitions. */
2455 Assert(outer_nparts == outer_rel->nparts);
2456 Assert(inner_nparts == inner_rel->nparts);
2457 max_nparts = Max(outer_nparts, inner_nparts);
2458 for (i = 0; i < max_nparts; i++)
2459 {
2460 if (i < outer_nparts)
2461 {
2462 int merged_index = outer_map->merged_indexes[i];
2463
2464 if (merged_index >= 0)
2465 {
2466 Assert(merged_index < nmerged);
2467 outer_indexes[merged_index] = i;
2468 }
2469 }
2470 if (i < inner_nparts)
2471 {
2472 int merged_index = inner_map->merged_indexes[i];
2473
2474 if (merged_index >= 0)
2475 {
2476 Assert(merged_index < nmerged);
2477 inner_indexes[merged_index] = i;
2478 }
2479 }
2480 }
2481
2482 /* Build the list pairs. */
2483 for (i = 0; i < nmerged; i++)
2484 {
2485 int outer_index = outer_indexes[i];
2486 int inner_index = inner_indexes[i];
2487
2488 /*
2489 * If both partitions are dummy, it means the merged partition that
2490 * had been assigned to the outer/inner partition was removed when
2491 * re-merging the outer/inner partition in
2492 * merge_matching_partitions(); ignore the merged partition.
2493 */
2494 if (outer_index == -1 && inner_index == -1)
2495 continue;
2496
2497 *outer_parts = lappend(*outer_parts, outer_index >= 0 ?
2498 outer_rel->part_rels[outer_index] : NULL);
2499 *inner_parts = lappend(*inner_parts, inner_index >= 0 ?
2500 inner_rel->part_rels[inner_index] : NULL);
2501 }
2502
2503 pfree(outer_indexes);
2504 pfree(inner_indexes);
2505}
#define Max(x, y)
Definition: c.h:1010

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

Referenced by merge_list_bounds(), and merge_range_bounds().

◆ get_hash_partition_greatest_modulus()

int get_hash_partition_greatest_modulus ( PartitionBoundInfo  bound)

Definition at line 3407 of file partbounds.c.

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

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

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

2713{
2714 Assert(compare_range_bounds(partnatts, partsupfuncs, partcollations,
2715 outer_lb, inner_lb) == lb_cmpval);
2716 Assert(compare_range_bounds(partnatts, partsupfuncs, partcollations,
2717 outer_ub, inner_ub) == ub_cmpval);
2718
2719 switch (jointype)
2720 {
2721 case JOIN_INNER:
2722 case JOIN_SEMI:
2723
2724 /*
2725 * An INNER/SEMI join will have the rows that fit both sides, so
2726 * the lower bound of the merged partition will be the higher of
2727 * the two lower bounds, and the upper bound of the merged
2728 * partition will be the lower of the two upper bounds.
2729 */
2730 *merged_lb = (lb_cmpval > 0) ? *outer_lb : *inner_lb;
2731 *merged_ub = (ub_cmpval < 0) ? *outer_ub : *inner_ub;
2732 break;
2733
2734 case JOIN_LEFT:
2735 case JOIN_ANTI:
2736
2737 /*
2738 * A LEFT/ANTI join will have all the rows from the outer side, so
2739 * the bounds of the merged partition will be the same as the
2740 * outer bounds.
2741 */
2742 *merged_lb = *outer_lb;
2743 *merged_ub = *outer_ub;
2744 break;
2745
2746 case JOIN_FULL:
2747
2748 /*
2749 * A FULL join will have all the rows from both sides, so the
2750 * lower bound of the merged partition will be the lower of the
2751 * two lower bounds, and the upper bound of the merged partition
2752 * will be the higher of the two upper bounds.
2753 */
2754 *merged_lb = (lb_cmpval < 0) ? *outer_lb : *inner_lb;
2755 *merged_ub = (ub_cmpval > 0) ? *outer_ub : *inner_ub;
2756 break;
2757
2758 default:
2759 elog(ERROR, "unrecognized join type: %d", (int) jointype);
2760 }
2761}
@ JOIN_SEMI
Definition: nodes.h:317
@ JOIN_FULL
Definition: nodes.h:305
@ JOIN_INNER
Definition: nodes.h:303
@ JOIN_LEFT
Definition: nodes.h:304
@ JOIN_ANTI
Definition: nodes.h:318

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

Referenced by merge_range_bounds().

◆ get_non_null_list_datum_count()

static int get_non_null_list_datum_count ( PartitionBoundSpec **  boundspecs,
int  nparts 
)
static

Definition at line 435 of file partbounds.c.

436{
437 int i;
438 int count = 0;
439
440 for (i = 0; i < nparts; i++)
441 {
442 ListCell *lc;
443
444 foreach(lc, boundspecs[i]->listdatums)
445 {
446 Const *val = lfirst_node(Const, lc);
447
448 if (!val->constisnull)
449 count++;
450 }
451 }
452
453 return count;
454}

References i, lfirst_node, and val.

Referenced by create_list_bounds().

◆ get_partition_bound_spec()

static PartitionBoundSpec * get_partition_bound_spec ( Oid  partOid)
static

Definition at line 5056 of file partbounds.c.

5057{
5058 HeapTuple tuple;
5059 Datum datum;
5060 bool isnull;
5061 PartitionBoundSpec *boundspec = NULL;
5062
5063 /* Try fetching the tuple from the catcache, for speed. */
5064 tuple = SearchSysCache1(RELOID, partOid);
5065 if (!HeapTupleIsValid(tuple))
5066 elog(ERROR, "cache lookup failed for relation %u", partOid);
5067
5068 datum = SysCacheGetAttr(RELOID, tuple,
5069 Anum_pg_class_relpartbound,
5070 &isnull);
5071 if (isnull)
5072 elog(ERROR, "partition bound for relation %u is null",
5073 partOid);
5074
5075 boundspec = stringToNode(TextDatumGetCString(datum));
5076
5077 if (!IsA(boundspec, PartitionBoundSpec))
5078 elog(ERROR, "expected PartitionBoundSpec for relation %u",
5079 partOid);
5080
5081 ReleaseSysCache(tuple);
5082 return boundspec;
5083}
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define IsA(nodeptr, _type_)
Definition: nodes.h:164
void * stringToNode(const char *str)
Definition: read.c:90
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:264
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:220
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:595

References elog, ERROR, HeapTupleIsValid, IsA, ReleaseSysCache(), SearchSysCache1(), stringToNode(), SysCacheGetAttr(), and TextDatumGetCString.

Referenced by calculate_partition_bound_for_merge(), and check_partition_bounds_for_split_range().

◆ get_partition_operator()

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

Definition at line 3824 of file partbounds.c.

3826{
3827 Oid operoid;
3828
3829 /*
3830 * Get the operator in the partitioning opfamily using the opclass'
3831 * declared input type as both left- and righttype.
3832 */
3833 operoid = get_opfamily_member(key->partopfamily[col],
3834 key->partopcintype[col],
3835 key->partopcintype[col],
3836 strategy);
3837 if (!OidIsValid(operoid))
3838 elog(ERROR, "missing operator %d(%u,%u) in partition opfamily %u",
3839 strategy, key->partopcintype[col], key->partopcintype[col],
3840 key->partopfamily[col]);
3841
3842 /*
3843 * If the partition key column is not of the same type as the operator
3844 * class and not polymorphic, tell caller to wrap the non-Const expression
3845 * in a RelabelType. This matches what parse_coerce.c does.
3846 */
3847 *need_relabel = (key->parttypid[col] != key->partopcintype[col] &&
3848 key->partopcintype[col] != RECORDOID &&
3849 !IsPolymorphicType(key->partopcintype[col]));
3850
3851 return operoid;
3852}
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:168

References elog, ERROR, get_opfamily_member(), sort-test::key, and OidIsValid.

Referenced by make_partition_op_expr().

◆ get_qual_for_hash()

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

Definition at line 3975 of file partbounds.c.

3976{
3978 FuncExpr *fexpr;
3979 Node *relidConst;
3980 Node *modulusConst;
3981 Node *remainderConst;
3982 List *args;
3983 ListCell *partexprs_item;
3984 int i;
3985
3986 /* Fixed arguments. */
3987 relidConst = (Node *) makeConst(OIDOID,
3988 -1,
3989 InvalidOid,
3990 sizeof(Oid),
3992 false,
3993 true);
3994
3995 modulusConst = (Node *) makeConst(INT4OID,
3996 -1,
3997 InvalidOid,
3998 sizeof(int32),
3999 Int32GetDatum(spec->modulus),
4000 false,
4001 true);
4002
4003 remainderConst = (Node *) makeConst(INT4OID,
4004 -1,
4005 InvalidOid,
4006 sizeof(int32),
4007 Int32GetDatum(spec->remainder),
4008 false,
4009 true);
4010
4011 args = list_make3(relidConst, modulusConst, remainderConst);
4012 partexprs_item = list_head(key->partexprs);
4013
4014 /* Add an argument for each key column. */
4015 for (i = 0; i < key->partnatts; i++)
4016 {
4017 Node *keyCol;
4018
4019 /* Left operand */
4020 if (key->partattrs[i] != 0)
4021 {
4022 keyCol = (Node *) makeVar(1,
4023 key->partattrs[i],
4024 key->parttypid[i],
4025 key->parttypmod[i],
4026 key->parttypcoll[i],
4027 0);
4028 }
4029 else
4030 {
4031 keyCol = (Node *) copyObject(lfirst(partexprs_item));
4032 partexprs_item = lnext(key->partexprs, partexprs_item);
4033 }
4034
4035 args = lappend(args, keyCol);
4036 }
4037
4038 fexpr = makeFuncExpr(F_SATISFIES_HASH_PARTITION,
4039 BOOLOID,
4040 args,
4041 InvalidOid,
4042 InvalidOid,
4044
4045 return list_make1(fexpr);
4046}
int32_t int32
Definition: c.h:548
Var * makeVar(int varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
FuncExpr * makeFuncExpr(Oid funcid, Oid rettype, List *args, Oid funccollid, Oid inputcollid, CoercionForm fformat)
Definition: makefuncs.c:594
#define copyObject(obj)
Definition: nodes.h:232
#define list_make1(x1)
Definition: pg_list.h:212
#define list_make3(x1, x2, x3)
Definition: pg_list.h:216
static ListCell * list_head(const List *l)
Definition: pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:262
#define InvalidOid
Definition: postgres_ext.h:37
@ COERCE_EXPLICIT_CALL
Definition: primnodes.h:766

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(), RelationGetPartitionKey(), RelationGetRelid, and PartitionBoundSpec::remainder.

Referenced by get_qual_from_partbound().

◆ get_qual_for_list()

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

Definition at line 4058 of file partbounds.c.

4059{
4061 List *result;
4062 Expr *keyCol;
4063 Expr *opexpr;
4064 NullTest *nulltest;
4065 ListCell *cell;
4066 List *elems = NIL;
4067 bool list_has_null = false;
4068
4069 /*
4070 * Only single-column list partitioning is supported, so we are worried
4071 * only about the partition key with index 0.
4072 */
4073 Assert(key->partnatts == 1);
4074
4075 /* Construct Var or expression representing the partition column */
4076 if (key->partattrs[0] != 0)
4077 keyCol = (Expr *) makeVar(1,
4078 key->partattrs[0],
4079 key->parttypid[0],
4080 key->parttypmod[0],
4081 key->parttypcoll[0],
4082 0);
4083 else
4084 keyCol = (Expr *) copyObject(linitial(key->partexprs));
4085
4086 /*
4087 * For default list partition, collect datums for all the partitions. The
4088 * default partition constraint should check that the partition key is
4089 * equal to none of those.
4090 */
4091 if (spec->is_default)
4092 {
4093 int i;
4094 int ndatums = 0;
4095 PartitionDesc pdesc = RelationGetPartitionDesc(parent, false);
4096 PartitionBoundInfo boundinfo = pdesc->boundinfo;
4097
4098 if (boundinfo)
4099 {
4100 ndatums = boundinfo->ndatums;
4101
4102 if (partition_bound_accepts_nulls(boundinfo))
4103 list_has_null = true;
4104 }
4105
4106 /*
4107 * If default is the only partition, there need not be any partition
4108 * constraint on it.
4109 */
4110 if (ndatums == 0 && !list_has_null)
4111 return NIL;
4112
4113 for (i = 0; i < ndatums; i++)
4114 {
4115 Const *val;
4116
4117 /*
4118 * Construct Const from known-not-null datum. We must be careful
4119 * to copy the value, because our result has to be able to outlive
4120 * the relcache entry we're copying from.
4121 */
4122 val = makeConst(key->parttypid[0],
4123 key->parttypmod[0],
4124 key->parttypcoll[0],
4125 key->parttyplen[0],
4126 datumCopy(*boundinfo->datums[i],
4127 key->parttypbyval[0],
4128 key->parttyplen[0]),
4129 false, /* isnull */
4130 key->parttypbyval[0]);
4131
4132 elems = lappend(elems, val);
4133 }
4134 }
4135 else
4136 {
4137 /*
4138 * Create list of Consts for the allowed values, excluding any nulls.
4139 */
4140 foreach(cell, spec->listdatums)
4141 {
4142 Const *val = lfirst_node(Const, cell);
4143
4144 if (val->constisnull)
4145 list_has_null = true;
4146 else
4147 elems = lappend(elems, copyObject(val));
4148 }
4149 }
4150
4151 if (elems)
4152 {
4153 /*
4154 * Generate the operator expression from the non-null partition
4155 * values.
4156 */
4158 keyCol, (Expr *) elems);
4159 }
4160 else
4161 {
4162 /*
4163 * If there are no partition values, we don't need an operator
4164 * expression.
4165 */
4166 opexpr = NULL;
4167 }
4168
4169 if (!list_has_null)
4170 {
4171 /*
4172 * Gin up a "col IS NOT NULL" test that will be ANDed with the main
4173 * expression. This might seem redundant, but the partition routing
4174 * machinery needs it.
4175 */
4176 nulltest = makeNode(NullTest);
4177 nulltest->arg = keyCol;
4178 nulltest->nulltesttype = IS_NOT_NULL;
4179 nulltest->argisrow = false;
4180 nulltest->location = -1;
4181
4182 result = opexpr ? list_make2(nulltest, opexpr) : list_make1(nulltest);
4183 }
4184 else
4185 {
4186 /*
4187 * Gin up a "col IS NULL" test that will be OR'd with the main
4188 * expression.
4189 */
4190 nulltest = makeNode(NullTest);
4191 nulltest->arg = keyCol;
4192 nulltest->nulltesttype = IS_NULL;
4193 nulltest->argisrow = false;
4194 nulltest->location = -1;
4195
4196 if (opexpr)
4197 {
4198 Expr *or;
4199
4200 or = makeBoolExpr(OR_EXPR, list_make2(nulltest, opexpr), -1);
4201 result = list_make1(or);
4202 }
4203 else
4204 result = list_make1(nulltest);
4205 }
4206
4207 /*
4208 * Note that, in general, applying NOT to a constraint expression doesn't
4209 * necessarily invert the set of rows it accepts, because NOT (NULL) is
4210 * NULL. However, the partition constraints we construct here never
4211 * evaluate to NULL, so applying NOT works as intended.
4212 */
4213 if (spec->is_default)
4214 {
4215 result = list_make1(make_ands_explicit(result));
4216 result = list_make1(makeBoolExpr(NOT_EXPR, result, -1));
4217 }
4218
4219 return result;
4220}
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:420
#define makeNode(_type_)
Definition: nodes.h:161
static Expr * make_partition_op_expr(PartitionKey key, int keynum, uint16 strategy, Expr *arg1, Expr *arg2)
Definition: partbounds.c:3860
#define list_make2(x1, x2)
Definition: pg_list.h:214
@ OR_EXPR
Definition: primnodes.h:963
@ NOT_EXPR
Definition: primnodes.h:963
@ IS_NULL
Definition: primnodes.h:1977
@ IS_NOT_NULL
Definition: primnodes.h:1977
#define BTEqualStrategyNumber
Definition: stratnum.h:31
NullTestType nulltesttype
Definition: primnodes.h:1984
ParseLoc location
Definition: primnodes.h:1987
Expr * arg
Definition: primnodes.h:1983

References NullTest::arg, Assert(), PartitionDescData::boundinfo, BTEqualStrategyNumber, copyObject, datumCopy(), PartitionBoundInfoData::datums, i, PartitionBoundSpec::is_default, IS_NOT_NULL, IS_NULL, sort-test::key, lappend(), lfirst_node, 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, partition_bound_accepts_nulls, RelationGetPartitionDesc(), RelationGetPartitionKey(), and val.

Referenced by check_default_partition_contents(), and get_qual_from_partbound().

◆ get_qual_for_range()

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

Definition at line 4267 of file partbounds.c.

4269{
4270 List *result = NIL;
4271 ListCell *cell1,
4272 *cell2,
4273 *partexprs_item,
4274 *partexprs_item_saved;
4275 int i,
4276 j;
4277 PartitionRangeDatum *ldatum,
4278 *udatum;
4280 Expr *keyCol;
4281 Const *lower_val,
4282 *upper_val;
4283 List *lower_or_arms,
4284 *upper_or_arms;
4285 int num_or_arms,
4286 current_or_arm;
4287 ListCell *lower_or_start_datum,
4288 *upper_or_start_datum;
4289 bool need_next_lower_arm,
4290 need_next_upper_arm;
4291
4292 if (spec->is_default)
4293 {
4294 List *or_expr_args = NIL;
4295 PartitionDesc pdesc = RelationGetPartitionDesc(parent, false);
4296 Oid *inhoids = pdesc->oids;
4297 int nparts = pdesc->nparts,
4298 k;
4299
4300 for (k = 0; k < nparts; k++)
4301 {
4302 Oid inhrelid = inhoids[k];
4303 HeapTuple tuple;
4304 Datum datum;
4305 PartitionBoundSpec *bspec;
4306
4307 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(inhrelid));
4308 if (!HeapTupleIsValid(tuple))
4309 elog(ERROR, "cache lookup failed for relation %u", inhrelid);
4310
4311 datum = SysCacheGetAttrNotNull(RELOID, tuple,
4312 Anum_pg_class_relpartbound);
4313 bspec = (PartitionBoundSpec *)
4315 if (!IsA(bspec, PartitionBoundSpec))
4316 elog(ERROR, "expected PartitionBoundSpec");
4317
4318 if (!bspec->is_default)
4319 {
4320 List *part_qual;
4321
4322 part_qual = get_qual_for_range(parent, bspec, true);
4323
4324 /*
4325 * AND the constraints of the partition and add to
4326 * or_expr_args
4327 */
4328 or_expr_args = lappend(or_expr_args, list_length(part_qual) > 1
4329 ? makeBoolExpr(AND_EXPR, part_qual, -1)
4330 : linitial(part_qual));
4331 }
4332 ReleaseSysCache(tuple);
4333 }
4334
4335 if (or_expr_args != NIL)
4336 {
4337 Expr *other_parts_constr;
4338
4339 /*
4340 * Combine the constraints obtained for non-default partitions
4341 * using OR. As requested, each of the OR's args doesn't include
4342 * the NOT NULL test for partition keys (which is to avoid its
4343 * useless repetition). Add the same now.
4344 */
4345 other_parts_constr =
4348 list_length(or_expr_args) > 1
4349 ? makeBoolExpr(OR_EXPR, or_expr_args,
4350 -1)
4351 : linitial(or_expr_args)),
4352 -1);
4353
4354 /*
4355 * Finally, the default partition contains everything *NOT*
4356 * contained in the non-default partitions.
4357 */
4359 list_make1(other_parts_constr), -1));
4360 }
4361
4362 return result;
4363 }
4364
4365 /*
4366 * If it is the recursive call for default, we skip the get_range_nulltest
4367 * to avoid accumulating the NullTest on the same keys for each partition.
4368 */
4369 if (!for_default)
4370 result = get_range_nulltest(key);
4371
4372 /*
4373 * Iterate over the key columns and check if the corresponding lower and
4374 * upper datums are equal using the btree equality operator for the
4375 * column's type. If equal, we emit single keyCol = common_value
4376 * expression. Starting from the first column for which the corresponding
4377 * lower and upper bound datums are not equal, we generate OR expressions
4378 * as shown in the function's header comment.
4379 */
4380 i = 0;
4381 partexprs_item = list_head(key->partexprs);
4382 partexprs_item_saved = partexprs_item; /* placate compiler */
4383 forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
4384 {
4385 EState *estate;
4386 MemoryContext oldcxt;
4387 Expr *test_expr;
4388 ExprState *test_exprstate;
4389 Datum test_result;
4390 bool isNull;
4391
4392 ldatum = lfirst_node(PartitionRangeDatum, cell1);
4393 udatum = lfirst_node(PartitionRangeDatum, cell2);
4394
4395 /*
4396 * Since get_range_key_properties() modifies partexprs_item, and we
4397 * might need to start over from the previous expression in the later
4398 * part of this function, save away the current value.
4399 */
4400 partexprs_item_saved = partexprs_item;
4401
4402 get_range_key_properties(key, i, ldatum, udatum,
4403 &partexprs_item,
4404 &keyCol,
4405 &lower_val, &upper_val);
4406
4407 /*
4408 * If either value is NULL, the corresponding partition bound is
4409 * either MINVALUE or MAXVALUE, and we treat them as unequal, because
4410 * even if they're the same, there is no common value to equate the
4411 * key column with.
4412 */
4413 if (!lower_val || !upper_val)
4414 break;
4415
4416 /* Create the test expression */
4417 estate = CreateExecutorState();
4418 oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
4420 (Expr *) lower_val,
4421 (Expr *) upper_val);
4422 fix_opfuncids((Node *) test_expr);
4423 test_exprstate = ExecInitExpr(test_expr, NULL);
4424 test_result = ExecEvalExprSwitchContext(test_exprstate,
4425 GetPerTupleExprContext(estate),
4426 &isNull);
4427 MemoryContextSwitchTo(oldcxt);
4428 FreeExecutorState(estate);
4429
4430 /* If not equal, go generate the OR expressions */
4431 if (!DatumGetBool(test_result))
4432 break;
4433
4434 /*
4435 * The bounds for the last key column can't be equal, because such a
4436 * range partition would never be allowed to be defined (it would have
4437 * an empty range otherwise).
4438 */
4439 if (i == key->partnatts - 1)
4440 elog(ERROR, "invalid range bound specification");
4441
4442 /* Equal, so generate keyCol = lower_val expression */
4443 result = lappend(result,
4445 keyCol, (Expr *) lower_val));
4446
4447 i++;
4448 }
4449
4450 /* First pair of lower_val and upper_val that are not equal. */
4451 lower_or_start_datum = cell1;
4452 upper_or_start_datum = cell2;
4453
4454 /* OR will have as many arms as there are key columns left. */
4455 num_or_arms = key->partnatts - i;
4456 current_or_arm = 0;
4457 lower_or_arms = upper_or_arms = NIL;
4458 need_next_lower_arm = need_next_upper_arm = true;
4459 while (current_or_arm < num_or_arms)
4460 {
4461 List *lower_or_arm_args = NIL,
4462 *upper_or_arm_args = NIL;
4463
4464 /* Restart scan of columns from the i'th one */
4465 j = i;
4466 partexprs_item = partexprs_item_saved;
4467
4468 for_both_cell(cell1, spec->lowerdatums, lower_or_start_datum,
4469 cell2, spec->upperdatums, upper_or_start_datum)
4470 {
4471 PartitionRangeDatum *ldatum_next = NULL,
4472 *udatum_next = NULL;
4473
4474 ldatum = lfirst_node(PartitionRangeDatum, cell1);
4475 if (lnext(spec->lowerdatums, cell1))
4476 ldatum_next = castNode(PartitionRangeDatum,
4477 lfirst(lnext(spec->lowerdatums, cell1)));
4478 udatum = lfirst_node(PartitionRangeDatum, cell2);
4479 if (lnext(spec->upperdatums, cell2))
4480 udatum_next = castNode(PartitionRangeDatum,
4481 lfirst(lnext(spec->upperdatums, cell2)));
4482 get_range_key_properties(key, j, ldatum, udatum,
4483 &partexprs_item,
4484 &keyCol,
4485 &lower_val, &upper_val);
4486
4487 if (need_next_lower_arm && lower_val)
4488 {
4489 uint16 strategy;
4490
4491 /*
4492 * For the non-last columns of this arm, use the EQ operator.
4493 * For the last column of this arm, use GT, unless this is the
4494 * last column of the whole bound check, or the next bound
4495 * datum is MINVALUE, in which case use GE.
4496 */
4497 if (j - i < current_or_arm)
4498 strategy = BTEqualStrategyNumber;
4499 else if (j == key->partnatts - 1 ||
4500 (ldatum_next &&
4501 ldatum_next->kind == PARTITION_RANGE_DATUM_MINVALUE))
4503 else
4504 strategy = BTGreaterStrategyNumber;
4505
4506 lower_or_arm_args = lappend(lower_or_arm_args,
4508 strategy,
4509 keyCol,
4510 (Expr *) lower_val));
4511 }
4512
4513 if (need_next_upper_arm && upper_val)
4514 {
4515 uint16 strategy;
4516
4517 /*
4518 * For the non-last columns of this arm, use the EQ operator.
4519 * For the last column of this arm, use LT, unless the next
4520 * bound datum is MAXVALUE, in which case use LE.
4521 */
4522 if (j - i < current_or_arm)
4523 strategy = BTEqualStrategyNumber;
4524 else if (udatum_next &&
4525 udatum_next->kind == PARTITION_RANGE_DATUM_MAXVALUE)
4526 strategy = BTLessEqualStrategyNumber;
4527 else
4528 strategy = BTLessStrategyNumber;
4529
4530 upper_or_arm_args = lappend(upper_or_arm_args,
4532 strategy,
4533 keyCol,
4534 (Expr *) upper_val));
4535 }
4536
4537 /*
4538 * Did we generate enough of OR's arguments? First arm considers
4539 * the first of the remaining columns, second arm considers first
4540 * two of the remaining columns, and so on.
4541 */
4542 ++j;
4543 if (j - i > current_or_arm)
4544 {
4545 /*
4546 * We must not emit any more arms if the new column that will
4547 * be considered is unbounded, or this one was.
4548 */
4549 if (!lower_val || !ldatum_next ||
4550 ldatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
4551 need_next_lower_arm = false;
4552 if (!upper_val || !udatum_next ||
4553 udatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
4554 need_next_upper_arm = false;
4555 break;
4556 }
4557 }
4558
4559 if (lower_or_arm_args != NIL)
4560 lower_or_arms = lappend(lower_or_arms,
4561 list_length(lower_or_arm_args) > 1
4562 ? makeBoolExpr(AND_EXPR, lower_or_arm_args, -1)
4563 : linitial(lower_or_arm_args));
4564
4565 if (upper_or_arm_args != NIL)
4566 upper_or_arms = lappend(upper_or_arms,
4567 list_length(upper_or_arm_args) > 1
4568 ? makeBoolExpr(AND_EXPR, upper_or_arm_args, -1)
4569 : linitial(upper_or_arm_args));
4570
4571 /* If no work to do in the next iteration, break away. */
4572 if (!need_next_lower_arm && !need_next_upper_arm)
4573 break;
4574
4575 ++current_or_arm;
4576 }
4577
4578 /*
4579 * Generate the OR expressions for each of lower and upper bounds (if
4580 * required), and append to the list of implicitly ANDed list of
4581 * expressions.
4582 */
4583 if (lower_or_arms != NIL)
4584 result = lappend(result,
4585 list_length(lower_or_arms) > 1
4586 ? makeBoolExpr(OR_EXPR, lower_or_arms, -1)
4587 : linitial(lower_or_arms));
4588 if (upper_or_arms != NIL)
4589 result = lappend(result,
4590 list_length(upper_or_arms) > 1
4591 ? makeBoolExpr(OR_EXPR, upper_or_arms, -1)
4592 : linitial(upper_or_arms));
4593
4594 /*
4595 * As noted above, for non-default, we return list with constant TRUE. If
4596 * the result is NIL during the recursive call for default, it implies
4597 * this is the only other partition which can hold every value of the key
4598 * except NULL. Hence we return the NullTest result skipped earlier.
4599 */
4600 if (result == NIL)
4601 result = for_default
4604
4605 return result;
4606}
uint16_t uint16
Definition: c.h:551
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:143
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:436
return false
Definition: isn.c:135
return true
Definition: isn.c:130
Node * makeBoolConst(bool value, bool isnull)
Definition: makefuncs.c:408
void fix_opfuncids(Node *node)
Definition: nodeFuncs.c:1837
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
@ PARTITION_RANGE_DATUM_MAXVALUE
Definition: parsenodes.h:954
@ PARTITION_RANGE_DATUM_MINVALUE
Definition: parsenodes.h:952
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:4624
static List * get_range_nulltest(PartitionKey key)
Definition: partbounds.c:4668
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
#define for_both_cell(cell1, list1, initcell1, cell2, list2, initcell2)
Definition: pg_list.h:540
static bool DatumGetBool(Datum X)
Definition: postgres.h:100
@ AND_EXPR
Definition: primnodes.h:963
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
#define BTLessStrategyNumber
Definition: stratnum.h:29
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32
MemoryContext es_query_cxt
Definition: execnodes.h:710
PartitionRangeDatumKind kind
Definition: parsenodes.h:961
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:625

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_qual_for_range(), get_range_key_properties(), get_range_nulltest(), GetPerTupleExprContext, HeapTupleIsValid, i, PartitionBoundSpec::is_default, IsA, j, sort-test::key, PartitionRangeDatum::kind, lappend(), lfirst, lfirst_node, linitial, list_head(), list_length(), list_make1, lnext(), PartitionBoundSpec::lowerdatums, make_partition_op_expr(), makeBoolConst(), makeBoolExpr(), MemoryContextSwitchTo(), NIL, NOT_EXPR, PartitionDescData::nparts, ObjectIdGetDatum(), PartitionDescData::oids, OR_EXPR, PARTITION_RANGE_DATUM_MAXVALUE, PARTITION_RANGE_DATUM_MINVALUE, PARTITION_RANGE_DATUM_VALUE, RelationGetPartitionDesc(), RelationGetPartitionKey(), ReleaseSysCache(), SearchSysCache1(), stringToNode(), SysCacheGetAttrNotNull(), TextDatumGetCString, and PartitionBoundSpec::upperdatums.

Referenced by check_default_partition_contents(), get_qual_for_range(), and get_qual_from_partbound().

◆ get_qual_from_partbound()

List * get_qual_from_partbound ( Relation  parent,
PartitionBoundSpec spec 
)

Definition at line 250 of file partbounds.c.

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

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

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

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

4630{
4631 /* Get partition key expression for this column */
4632 if (key->partattrs[keynum] != 0)
4633 {
4634 *keyCol = (Expr *) makeVar(1,
4635 key->partattrs[keynum],
4636 key->parttypid[keynum],
4637 key->parttypmod[keynum],
4638 key->parttypcoll[keynum],
4639 0);
4640 }
4641 else
4642 {
4643 if (*partexprs_item == NULL)
4644 elog(ERROR, "wrong number of partition key expressions");
4645 *keyCol = copyObject(lfirst(*partexprs_item));
4646 *partexprs_item = lnext(key->partexprs, *partexprs_item);
4647 }
4648
4649 /* Get appropriate Const nodes for the bounds */
4650 if (ldatum->kind == PARTITION_RANGE_DATUM_VALUE)
4651 *lower_val = castNode(Const, copyObject(ldatum->value));
4652 else
4653 *lower_val = NULL;
4654
4655 if (udatum->kind == PARTITION_RANGE_DATUM_VALUE)
4656 *upper_val = castNode(Const, copyObject(udatum->value));
4657 else
4658 *upper_val = NULL;
4659}

References castNode, copyObject, elog, ERROR, sort-test::key, PartitionRangeDatum::kind, lfirst, lnext(), makeVar(), PARTITION_RANGE_DATUM_VALUE, and PartitionRangeDatum::value.

Referenced by get_qual_for_range().

◆ get_range_nulltest()

static List * get_range_nulltest ( PartitionKey  key)
static

Definition at line 4668 of file partbounds.c.

4669{
4670 List *result = NIL;
4671 NullTest *nulltest;
4672 ListCell *partexprs_item;
4673 int i;
4674
4675 partexprs_item = list_head(key->partexprs);
4676 for (i = 0; i < key->partnatts; i++)
4677 {
4678 Expr *keyCol;
4679
4680 if (key->partattrs[i] != 0)
4681 {
4682 keyCol = (Expr *) makeVar(1,
4683 key->partattrs[i],
4684 key->parttypid[i],
4685 key->parttypmod[i],
4686 key->parttypcoll[i],
4687 0);
4688 }
4689 else
4690 {
4691 if (partexprs_item == NULL)
4692 elog(ERROR, "wrong number of partition key expressions");
4693 keyCol = copyObject(lfirst(partexprs_item));
4694 partexprs_item = lnext(key->partexprs, partexprs_item);
4695 }
4696
4697 nulltest = makeNode(NullTest);
4698 nulltest->arg = keyCol;
4699 nulltest->nulltesttype = IS_NOT_NULL;
4700 nulltest->argisrow = false;
4701 nulltest->location = -1;
4702 result = lappend(result, nulltest);
4703 }
4704
4705 return result;
4706}

References NullTest::arg, copyObject, elog, ERROR, i, IS_NOT_NULL, sort-test::key, lappend(), lfirst, list_head(), lnext(), NullTest::location, makeNode, makeVar(), NIL, and NullTest::nulltesttype.

Referenced by get_qual_for_range().

◆ get_range_partition()

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

Definition at line 2574 of file partbounds.c.

2579{
2580 int part_index;
2581
2583
2584 do
2585 {
2586 part_index = get_range_partition_internal(bi, lb_pos, lb, ub);
2587 if (part_index == -1)
2588 return -1;
2589 } while (is_dummy_partition(rel, part_index));
2590
2591 return part_index;
2592}
static bool is_dummy_partition(RelOptInfo *rel, int part_index)
Definition: partbounds.c:1837
static int get_range_partition_internal(PartitionBoundInfo bi, int *lb_pos, PartitionRangeBound *lb, PartitionRangeBound *ub)
Definition: partbounds.c:2595

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

Referenced by merge_range_bounds().

◆ get_range_partition_internal()

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

Definition at line 2595 of file partbounds.c.

2599{
2600 /* Return the index as -1 if we've exhausted all lower bounds. */
2601 if (*lb_pos >= bi->ndatums)
2602 return -1;
2603
2604 /* A lower bound should have at least one more bound after it. */
2605 Assert(*lb_pos + 1 < bi->ndatums);
2606
2607 /* Set the lower bound. */
2608 lb->index = bi->indexes[*lb_pos];
2609 lb->datums = bi->datums[*lb_pos];
2610 lb->kind = bi->kind[*lb_pos];
2611 lb->lower = true;
2612 /* Set the upper bound. */
2613 ub->index = bi->indexes[*lb_pos + 1];
2614 ub->datums = bi->datums[*lb_pos + 1];
2615 ub->kind = bi->kind[*lb_pos + 1];
2616 ub->lower = false;
2617
2618 /* The index assigned to an upper bound should be valid. */
2619 Assert(ub->index >= 0);
2620
2621 /*
2622 * Advance the position to the next lower bound. If there are no bounds
2623 * left beyond the upper bound, we have reached the last lower bound.
2624 */
2625 if (*lb_pos + 2 >= bi->ndatums)
2626 *lb_pos = bi->ndatums;
2627 else
2628 {
2629 /*
2630 * If the index assigned to the bound next to the upper bound isn't
2631 * valid, that is the next lower bound; else, the upper bound is also
2632 * the lower bound of the next range partition.
2633 */
2634 if (bi->indexes[*lb_pos + 2] < 0)
2635 *lb_pos = *lb_pos + 2;
2636 else
2637 *lb_pos = *lb_pos + 1;
2638 }
2639
2640 return ub->index;
2641}

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

Referenced by get_range_partition().

◆ init_partition_map()

static void init_partition_map ( RelOptInfo rel,
PartitionMap map 
)
static

Definition at line 1805 of file partbounds.c.

1806{
1807 int nparts = rel->nparts;
1808 int i;
1809
1810 map->nparts = nparts;
1811 map->merged_indexes = palloc_array(int, nparts);
1812 map->merged = palloc_array(bool, nparts);
1813 map->did_remapping = false;
1814 map->old_indexes = palloc_array(int, nparts);
1815 for (i = 0; i < nparts; i++)
1816 {
1817 map->merged_indexes[i] = map->old_indexes[i] = -1;
1818 map->merged[i] = false;
1819 }
1820}

References PartitionMap::did_remapping, i, PartitionMap::merged, PartitionMap::merged_indexes, PartitionMap::nparts, RelOptInfo::nparts, PartitionMap::old_indexes, and palloc_array.

Referenced by merge_list_bounds(), and merge_range_bounds().

◆ is_dummy_partition()

static bool is_dummy_partition ( RelOptInfo rel,
int  part_index 
)
static

Definition at line 1837 of file partbounds.c.

1838{
1839 RelOptInfo *part_rel;
1840
1841 Assert(part_index >= 0);
1842 part_rel = rel->part_rels[part_index];
1843 if (part_rel == NULL || IS_DUMMY_REL(part_rel))
1844 return true;
1845 return false;
1846}
#define IS_DUMMY_REL(r)
Definition: pathnodes.h:2194

References Assert(), and IS_DUMMY_REL.

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

◆ make_one_partition_rbound()

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

Definition at line 3421 of file partbounds.c.

3422{
3423 PartitionRangeBound *bound;
3424 ListCell *lc;
3425 int i;
3426
3427 Assert(datums != NIL);
3428
3430 bound->index = index;
3431 bound->datums = palloc0_array(Datum, key->partnatts);
3432 bound->kind = palloc0_array(PartitionRangeDatumKind, key->partnatts);
3433 bound->lower = lower;
3434
3435 i = 0;
3436 foreach(lc, datums)
3437 {
3439
3440 /* What's contained in this range datum? */
3441 bound->kind[i] = datum->kind;
3442
3443 if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
3444 {
3445 Const *val = castNode(Const, datum->value);
3446
3447 if (val->constisnull)
3448 elog(ERROR, "invalid range bound datum");
3449 bound->datums[i] = val->constvalue;
3450 }
3451
3452 i++;
3453 }
3454
3455 return bound;
3456}

References Assert(), castNode, PartitionRangeBound::datums, elog, ERROR, i, PartitionRangeBound::index, sort-test::key, PartitionRangeBound::kind, PartitionRangeDatum::kind, lfirst_node, PartitionRangeBound::lower, lower(), NIL, palloc0_array, palloc0_object, PARTITION_RANGE_DATUM_VALUE, val, and PartitionRangeDatum::value.

Referenced by calculate_partition_bound_for_merge(), check_new_partition_bound(), check_partition_bounds_for_split_range(), check_partitions_for_split(), check_two_partitions_bounds_range(), and create_range_bounds().

◆ make_partition_op_expr()

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

Definition at line 3860 of file partbounds.c.

3862{
3863 Oid operoid;
3864 bool need_relabel = false;
3865 Expr *result = NULL;
3866
3867 /* Get the correct btree operator for this partitioning column */
3868 operoid = get_partition_operator(key, keynum, strategy, &need_relabel);
3869
3870 /*
3871 * Chosen operator may be such that the non-Const operand needs to be
3872 * coerced, so apply the same; see the comment in
3873 * get_partition_operator().
3874 */
3875 if (!IsA(arg1, Const) &&
3876 (need_relabel ||
3877 key->partcollation[keynum] != key->parttypcoll[keynum]))
3878 arg1 = (Expr *) makeRelabelType(arg1,
3879 key->partopcintype[keynum],
3880 -1,
3881 key->partcollation[keynum],
3883
3884 /* Generate the actual expression */
3885 switch (key->strategy)
3886 {
3888 {
3889 List *elems = (List *) arg2;
3890 int nelems = list_length(elems);
3891
3892 Assert(nelems >= 1);
3893 Assert(keynum == 0);
3894
3895 if (nelems > 1 &&
3896 !type_is_array(key->parttypid[keynum]))
3897 {
3898 ArrayExpr *arrexpr;
3899 ScalarArrayOpExpr *saopexpr;
3900
3901 /* Construct an ArrayExpr for the right-hand inputs */
3902 arrexpr = makeNode(ArrayExpr);
3903 arrexpr->array_typeid =
3904 get_array_type(key->parttypid[keynum]);
3905 arrexpr->array_collid = key->parttypcoll[keynum];
3906 arrexpr->element_typeid = key->parttypid[keynum];
3907 arrexpr->elements = elems;
3908 arrexpr->multidims = false;
3909 arrexpr->location = -1;
3910
3911 /* Build leftop = ANY (rightop) */
3912 saopexpr = makeNode(ScalarArrayOpExpr);
3913 saopexpr->opno = operoid;
3914 saopexpr->opfuncid = get_opcode(operoid);
3915 saopexpr->hashfuncid = InvalidOid;
3916 saopexpr->negfuncid = InvalidOid;
3917 saopexpr->useOr = true;
3918 saopexpr->inputcollid = key->partcollation[keynum];
3919 saopexpr->args = list_make2(arg1, arrexpr);
3920 saopexpr->location = -1;
3921
3922 result = (Expr *) saopexpr;
3923 }
3924 else
3925 {
3926 List *elemops = NIL;
3927 ListCell *lc;
3928
3929 foreach(lc, elems)
3930 {
3931 Expr *elem = lfirst(lc),
3932 *elemop;
3933
3934 elemop = make_opclause(operoid,
3935 BOOLOID,
3936 false,
3937 arg1, elem,
3938 InvalidOid,
3939 key->partcollation[keynum]);
3940 elemops = lappend(elemops, elemop);
3941 }
3942
3943 result = nelems > 1 ? makeBoolExpr(OR_EXPR, elemops, -1) : linitial(elemops);
3944 }
3945 break;
3946 }
3947
3949 result = make_opclause(operoid,
3950 BOOLOID,
3951 false,
3952 arg1, arg2,
3953 InvalidOid,
3954 key->partcollation[keynum]);
3955 break;
3956
3958 Assert(false);
3959 break;
3960 }
3961
3962 return result;
3963}
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1450
Oid get_array_type(Oid typid)
Definition: lsyscache.c:2952
#define type_is_array(typid)
Definition: lsyscache.h:214
RelabelType * makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod, Oid rcollid, CoercionForm rformat)
Definition: makefuncs.c:453
Expr * make_opclause(Oid opno, Oid opresulttype, bool opretset, Expr *leftop, Expr *rightop, Oid opcollid, Oid inputcollid)
Definition: makefuncs.c:701
static Oid get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel)
Definition: partbounds.c:3824
@ COERCE_EXPLICIT_CAST
Definition: primnodes.h:767
ParseLoc location
Definition: primnodes.h:1421
ParseLoc location
Definition: primnodes.h:951

References ScalarArrayOpExpr::args, Assert(), COERCE_EXPLICIT_CAST, get_array_type(), get_opcode(), get_partition_operator(), InvalidOid, IsA, sort-test::key, lappend(), lfirst, linitial, list_length(), list_make2, ScalarArrayOpExpr::location, ArrayExpr::location, make_opclause(), makeBoolExpr(), makeNode, makeRelabelType(), NIL, ScalarArrayOpExpr::opno, OR_EXPR, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, type_is_array, and ScalarArrayOpExpr::useOr.

Referenced by get_qual_for_list(), and get_qual_for_range().

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

2260{
2261 int outer_merged_index = -1;
2262 int inner_merged_index = -1;
2263
2264 Assert(outer_has_default || inner_has_default);
2265
2266 /* Get the merged partition indexes for the default partitions. */
2267 if (outer_has_default)
2268 {
2269 Assert(outer_default >= 0 && outer_default < outer_map->nparts);
2270 outer_merged_index = outer_map->merged_indexes[outer_default];
2271 }
2272 if (inner_has_default)
2273 {
2274 Assert(inner_default >= 0 && inner_default < inner_map->nparts);
2275 inner_merged_index = inner_map->merged_indexes[inner_default];
2276 }
2277
2278 if (outer_has_default && !inner_has_default)
2279 {
2280 /*
2281 * If this is an outer join, the default partition on the outer side
2282 * has to be scanned all the way anyway; if we have not yet assigned a
2283 * partition, merge the default partition with a dummy partition on
2284 * the other side. The merged partition will act as the default
2285 * partition of the join relation (see comments in
2286 * process_inner_partition()).
2287 */
2288 if (IS_OUTER_JOIN(jointype))
2289 {
2290 Assert(jointype != JOIN_RIGHT);
2291 if (outer_merged_index == -1)
2292 {
2293 Assert(*default_index == -1);
2294 *default_index = merge_partition_with_dummy(outer_map,
2295 outer_default,
2296 next_index);
2297 }
2298 else
2299 Assert(*default_index == outer_merged_index);
2300 }
2301 else
2302 Assert(*default_index == -1);
2303 }
2304 else if (!outer_has_default && inner_has_default)
2305 {
2306 /*
2307 * If this is a FULL join, the default partition on the inner side has
2308 * to be scanned all the way anyway; if we have not yet assigned a
2309 * partition, merge the default partition with a dummy partition on
2310 * the other side. The merged partition will act as the default
2311 * partition of the join relation (see comments in
2312 * process_outer_partition()).
2313 */
2314 if (jointype == JOIN_FULL)
2315 {
2316 if (inner_merged_index == -1)
2317 {
2318 Assert(*default_index == -1);
2319 *default_index = merge_partition_with_dummy(inner_map,
2320 inner_default,
2321 next_index);
2322 }
2323 else
2324 Assert(*default_index == inner_merged_index);
2325 }
2326 else
2327 Assert(*default_index == -1);
2328 }
2329 else
2330 {
2331 Assert(outer_has_default && inner_has_default);
2332
2333 /*
2334 * The default partitions have to be joined with each other, so merge
2335 * them. Note that each of the default partitions isn't merged yet
2336 * (see, process_outer_partition()/process_inner_partition()), so they
2337 * should be merged successfully. The merged partition will act as
2338 * the default partition of the join relation.
2339 */
2340 Assert(outer_merged_index == -1);
2341 Assert(inner_merged_index == -1);
2342 Assert(*default_index == -1);
2343 *default_index = merge_matching_partitions(outer_map,
2344 inner_map,
2345 outer_default,
2346 inner_default,
2347 next_index);
2348 Assert(*default_index >= 0);
2349 }
2350}
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:348
@ JOIN_RIGHT
Definition: nodes.h:306
static int merge_matching_partitions(PartitionMap *outer_map, PartitionMap *inner_map, int outer_index, int inner_index, int *next_index)
Definition: partbounds.c:1856
static int merge_partition_with_dummy(PartitionMap *map, int index, int *next_index)
Definition: partbounds.c:2361

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

◆ merge_list_bounds()

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

Definition at line 1192 of file partbounds.c.

1196{
1197 PartitionBoundInfo merged_bounds = NULL;
1198 PartitionBoundInfo outer_bi = outer_rel->boundinfo;
1199 PartitionBoundInfo inner_bi = inner_rel->boundinfo;
1200 bool outer_has_default = partition_bound_has_default(outer_bi);
1201 bool inner_has_default = partition_bound_has_default(inner_bi);
1202 int outer_default = outer_bi->default_index;
1203 int inner_default = inner_bi->default_index;
1204 bool outer_has_null = partition_bound_accepts_nulls(outer_bi);
1205 bool inner_has_null = partition_bound_accepts_nulls(inner_bi);
1206 PartitionMap outer_map;
1207 PartitionMap inner_map;
1208 int outer_pos;
1209 int inner_pos;
1210 int next_index = 0;
1211 int null_index = -1;
1212 int default_index = -1;
1213 List *merged_datums = NIL;
1214 List *merged_indexes = NIL;
1215
1216 Assert(*outer_parts == NIL);
1217 Assert(*inner_parts == NIL);
1218 Assert(outer_bi->strategy == inner_bi->strategy &&
1219 outer_bi->strategy == PARTITION_STRATEGY_LIST);
1220 /* List partitioning doesn't require kinds. */
1221 Assert(!outer_bi->kind && !inner_bi->kind);
1222
1223 init_partition_map(outer_rel, &outer_map);
1224 init_partition_map(inner_rel, &inner_map);
1225
1226 /*
1227 * If the default partitions (if any) have been proven empty, deem them
1228 * non-existent.
1229 */
1230 if (outer_has_default && is_dummy_partition(outer_rel, outer_default))
1231 outer_has_default = false;
1232 if (inner_has_default && is_dummy_partition(inner_rel, inner_default))
1233 inner_has_default = false;
1234
1235 /*
1236 * Merge partitions from both sides. In each iteration we compare a pair
1237 * of list values, one from each side, and decide whether the
1238 * corresponding partitions match or not. If the two values match
1239 * exactly, move to the next pair of list values, otherwise move to the
1240 * next list value on the side with a smaller list value.
1241 */
1242 outer_pos = inner_pos = 0;
1243 while (outer_pos < outer_bi->ndatums || inner_pos < inner_bi->ndatums)
1244 {
1245 int outer_index = -1;
1246 int inner_index = -1;
1247 Datum *outer_datums;
1248 Datum *inner_datums;
1249 int cmpval;
1250 Datum *merged_datum = NULL;
1251 int merged_index = -1;
1252
1253 if (outer_pos < outer_bi->ndatums)
1254 {
1255 /*
1256 * If the partition on the outer side has been proven empty,
1257 * ignore it and move to the next datum on the outer side.
1258 */
1259 outer_index = outer_bi->indexes[outer_pos];
1260 if (is_dummy_partition(outer_rel, outer_index))
1261 {
1262 outer_pos++;
1263 continue;
1264 }
1265 }
1266 if (inner_pos < inner_bi->ndatums)
1267 {
1268 /*
1269 * If the partition on the inner side has been proven empty,
1270 * ignore it and move to the next datum on the inner side.
1271 */
1272 inner_index = inner_bi->indexes[inner_pos];
1273 if (is_dummy_partition(inner_rel, inner_index))
1274 {
1275 inner_pos++;
1276 continue;
1277 }
1278 }
1279
1280 /* Get the list values. */
1281 outer_datums = outer_pos < outer_bi->ndatums ?
1282 outer_bi->datums[outer_pos] : NULL;
1283 inner_datums = inner_pos < inner_bi->ndatums ?
1284 inner_bi->datums[inner_pos] : NULL;
1285
1286 /*
1287 * We run this loop till both sides finish. This allows us to avoid
1288 * duplicating code to handle the remaining values on the side which
1289 * finishes later. For that we set the comparison parameter cmpval in
1290 * such a way that it appears as if the side which finishes earlier
1291 * has an extra value higher than any other value on the unfinished
1292 * side. That way we advance the values on the unfinished side till
1293 * all of its values are exhausted.
1294 */
1295 if (outer_pos >= outer_bi->ndatums)
1296 cmpval = 1;
1297 else if (inner_pos >= inner_bi->ndatums)
1298 cmpval = -1;
1299 else
1300 {
1301 Assert(outer_datums != NULL && inner_datums != NULL);
1302 cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
1303 partcollation[0],
1304 outer_datums[0],
1305 inner_datums[0]));
1306 }
1307
1308 if (cmpval == 0)
1309 {
1310 /* Two list values match exactly. */
1311 Assert(outer_pos < outer_bi->ndatums);
1312 Assert(inner_pos < inner_bi->ndatums);
1313 Assert(outer_index >= 0);
1314 Assert(inner_index >= 0);
1315
1316 /*
1317 * Try merging both partitions. If successful, add the list value
1318 * and index of the merged partition below.
1319 */
1320 merged_index = merge_matching_partitions(&outer_map, &inner_map,
1321 outer_index, inner_index,
1322 &next_index);
1323 if (merged_index == -1)
1324 goto cleanup;
1325
1326 merged_datum = outer_datums;
1327
1328 /* Move to the next pair of list values. */
1329 outer_pos++;
1330 inner_pos++;
1331 }
1332 else if (cmpval < 0)
1333 {
1334 /* A list value missing from the inner side. */
1335 Assert(outer_pos < outer_bi->ndatums);
1336
1337 /*
1338 * If the inner side has the default partition, or this is an
1339 * outer join, try to assign a merged partition to the outer
1340 * partition (see process_outer_partition()). Otherwise, the
1341 * outer partition will not contribute to the result.
1342 */
1343 if (inner_has_default || IS_OUTER_JOIN(jointype))
1344 {
1345 /* Get the outer partition. */
1346 outer_index = outer_bi->indexes[outer_pos];
1347 Assert(outer_index >= 0);
1348 merged_index = process_outer_partition(&outer_map,
1349 &inner_map,
1350 outer_has_default,
1351 inner_has_default,
1352 outer_index,
1353 inner_default,
1354 jointype,
1355 &next_index,
1356 &default_index);
1357 if (merged_index == -1)
1358 goto cleanup;
1359 merged_datum = outer_datums;
1360 }
1361
1362 /* Move to the next list value on the outer side. */
1363 outer_pos++;
1364 }
1365 else
1366 {
1367 /* A list value missing from the outer side. */
1368 Assert(cmpval > 0);
1369 Assert(inner_pos < inner_bi->ndatums);
1370
1371 /*
1372 * If the outer side has the default partition, or this is a FULL
1373 * join, try to assign a merged partition to the inner partition
1374 * (see process_inner_partition()). Otherwise, the inner
1375 * partition will not contribute to the result.
1376 */
1377 if (outer_has_default || jointype == JOIN_FULL)
1378 {
1379 /* Get the inner partition. */
1380 inner_index = inner_bi->indexes[inner_pos];
1381 Assert(inner_index >= 0);
1382 merged_index = process_inner_partition(&outer_map,
1383 &inner_map,
1384 outer_has_default,
1385 inner_has_default,
1386 inner_index,
1387 outer_default,
1388 jointype,
1389 &next_index,
1390 &default_index);
1391 if (merged_index == -1)
1392 goto cleanup;
1393 merged_datum = inner_datums;
1394 }
1395
1396 /* Move to the next list value on the inner side. */
1397 inner_pos++;
1398 }
1399
1400 /*
1401 * If we assigned a merged partition, add the list value and index of
1402 * the merged partition if appropriate.
1403 */
1404 if (merged_index >= 0 && merged_index != default_index)
1405 {
1406 merged_datums = lappend(merged_datums, merged_datum);
1407 merged_indexes = lappend_int(merged_indexes, merged_index);
1408 }
1409 }
1410
1411 /*
1412 * If the NULL partitions (if any) have been proven empty, deem them
1413 * non-existent.
1414 */
1415 if (outer_has_null &&
1416 is_dummy_partition(outer_rel, outer_bi->null_index))
1417 outer_has_null = false;
1418 if (inner_has_null &&
1419 is_dummy_partition(inner_rel, inner_bi->null_index))
1420 inner_has_null = false;
1421
1422 /* Merge the NULL partitions if any. */
1423 if (outer_has_null || inner_has_null)
1424 merge_null_partitions(&outer_map, &inner_map,
1425 outer_has_null, inner_has_null,
1426 outer_bi->null_index, inner_bi->null_index,
1427 jointype, &next_index, &null_index);
1428 else
1429 Assert(null_index == -1);
1430
1431 /* Merge the default partitions if any. */
1432 if (outer_has_default || inner_has_default)
1433 merge_default_partitions(&outer_map, &inner_map,
1434 outer_has_default, inner_has_default,
1435 outer_default, inner_default,
1436 jointype, &next_index, &default_index);
1437 else
1438 Assert(default_index == -1);
1439
1440 /* If we have merged partitions, create the partition bounds. */
1441 if (next_index > 0)
1442 {
1443 /* Fix the merged_indexes list if necessary. */
1444 if (outer_map.did_remapping || inner_map.did_remapping)
1445 {
1446 Assert(jointype == JOIN_FULL);
1447 fix_merged_indexes(&outer_map, &inner_map,
1448 next_index, merged_indexes);
1449 }
1450
1451 /* Use maps to match partitions from inputs. */
1452 generate_matching_part_pairs(outer_rel, inner_rel,
1453 &outer_map, &inner_map,
1454 next_index,
1455 outer_parts, inner_parts);
1456 Assert(*outer_parts != NIL);
1457 Assert(*inner_parts != NIL);
1458 Assert(list_length(*outer_parts) == list_length(*inner_parts));
1459 Assert(list_length(*outer_parts) <= next_index);
1460
1461 /* Make a PartitionBoundInfo struct to return. */
1462 merged_bounds = build_merged_partition_bounds(outer_bi->strategy,
1463 merged_datums,
1464 NIL,
1465 merged_indexes,
1466 null_index,
1467 default_index);
1468 Assert(merged_bounds);
1469 }
1470
1471cleanup:
1472 /* Free local memory before returning. */
1473 list_free(merged_datums);
1474 list_free(merged_indexes);
1475 free_partition_map(&outer_map);
1476 free_partition_map(&inner_map);
1477
1478 return merged_bounds;
1479}
static void cleanup(void)
Definition: bootstrap.c:715
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:2056
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:2251
static void init_partition_map(RelOptInfo *rel, PartitionMap *map)
Definition: partbounds.c:1805
static void free_partition_map(PartitionMap *map)
Definition: partbounds.c:1826
static void fix_merged_indexes(PartitionMap *outer_map, PartitionMap *inner_map, int nmerged, List *merged_indexes)
Definition: partbounds.c:2379
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:2433
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:2512
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:2141
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:1974

References Assert(), 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().

◆ merge_matching_partitions()

static int merge_matching_partitions ( PartitionMap outer_map,
PartitionMap inner_map,
int  outer_index,
int  inner_index,
int *  next_index 
)
static

Definition at line 1856 of file partbounds.c.

1858{
1859 int outer_merged_index;
1860 int inner_merged_index;
1861 bool outer_merged;
1862 bool inner_merged;
1863
1864 Assert(outer_index >= 0 && outer_index < outer_map->nparts);
1865 outer_merged_index = outer_map->merged_indexes[outer_index];
1866 outer_merged = outer_map->merged[outer_index];
1867 Assert(inner_index >= 0 && inner_index < inner_map->nparts);
1868 inner_merged_index = inner_map->merged_indexes[inner_index];
1869 inner_merged = inner_map->merged[inner_index];
1870
1871 /*
1872 * Handle cases where we have already assigned a merged partition to each
1873 * of the given partitions.
1874 */
1875 if (outer_merged_index >= 0 && inner_merged_index >= 0)
1876 {
1877 /*
1878 * If the merged partitions are the same, no need to do anything;
1879 * return the index of the merged partitions. Otherwise, if each of
1880 * the given partitions has been merged with a dummy partition on the
1881 * other side, re-map them to either of the two merged partitions.
1882 * Otherwise, they can't be merged, so return -1.
1883 */
1884 if (outer_merged_index == inner_merged_index)
1885 {
1886 Assert(outer_merged);
1887 Assert(inner_merged);
1888 return outer_merged_index;
1889 }
1890 if (!outer_merged && !inner_merged)
1891 {
1892 /*
1893 * This can only happen for a list-partitioning case. We re-map
1894 * them to the merged partition with the smaller of the two merged
1895 * indexes to preserve the property that the canonical order of
1896 * list partitions is determined by the indexes assigned to the
1897 * smallest list value of each partition.
1898 */
1899 if (outer_merged_index < inner_merged_index)
1900 {
1901 outer_map->merged[outer_index] = true;
1902 inner_map->merged_indexes[inner_index] = outer_merged_index;
1903 inner_map->merged[inner_index] = true;
1904 inner_map->did_remapping = true;
1905 inner_map->old_indexes[inner_index] = inner_merged_index;
1906 return outer_merged_index;
1907 }
1908 else
1909 {
1910 inner_map->merged[inner_index] = true;
1911 outer_map->merged_indexes[outer_index] = inner_merged_index;
1912 outer_map->merged[outer_index] = true;
1913 outer_map->did_remapping = true;
1914 outer_map->old_indexes[outer_index] = outer_merged_index;
1915 return inner_merged_index;
1916 }
1917 }
1918 return -1;
1919 }
1920
1921 /* At least one of the given partitions should not have yet been merged. */
1922 Assert(outer_merged_index == -1 || inner_merged_index == -1);
1923
1924 /*
1925 * If neither of them has been merged, merge them. Otherwise, if one has
1926 * been merged with a dummy partition on the other side (and the other
1927 * hasn't yet been merged with anything), re-merge them. Otherwise, they
1928 * can't be merged, so return -1.
1929 */
1930 if (outer_merged_index == -1 && inner_merged_index == -1)
1931 {
1932 int merged_index = *next_index;
1933
1934 Assert(!outer_merged);
1935 Assert(!inner_merged);
1936 outer_map->merged_indexes[outer_index] = merged_index;
1937 outer_map->merged[outer_index] = true;
1938 inner_map->merged_indexes[inner_index] = merged_index;
1939 inner_map->merged[inner_index] = true;
1940 *next_index = *next_index + 1;
1941 return merged_index;
1942 }
1943 if (outer_merged_index >= 0 && !outer_map->merged[outer_index])
1944 {
1945 Assert(inner_merged_index == -1);
1946 Assert(!inner_merged);
1947 inner_map->merged_indexes[inner_index] = outer_merged_index;
1948 inner_map->merged[inner_index] = true;
1949 outer_map->merged[outer_index] = true;
1950 return outer_merged_index;
1951 }
1952 if (inner_merged_index >= 0 && !inner_map->merged[inner_index])
1953 {
1954 Assert(outer_merged_index == -1);
1955 Assert(!outer_merged);
1956 outer_map->merged_indexes[outer_index] = inner_merged_index;
1957 outer_map->merged[outer_index] = true;
1958 inner_map->merged[inner_index] = true;
1959 return inner_merged_index;
1960 }
1961 return -1;
1962}

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

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

2150{
2151 bool consider_outer_null = false;
2152 bool consider_inner_null = false;
2153
2154 Assert(outer_has_null || inner_has_null);
2155 Assert(*null_index == -1);
2156
2157 /*
2158 * Check whether the NULL partitions have already been merged and if so,
2159 * set the consider_outer_null/consider_inner_null flags.
2160 */
2161 if (outer_has_null)
2162 {
2163 Assert(outer_null >= 0 && outer_null < outer_map->nparts);
2164 if (outer_map->merged_indexes[outer_null] == -1)
2165 consider_outer_null = true;
2166 }
2167 if (inner_has_null)
2168 {
2169 Assert(inner_null >= 0 && inner_null < inner_map->nparts);
2170 if (inner_map->merged_indexes[inner_null] == -1)
2171 consider_inner_null = true;
2172 }
2173
2174 /* If both flags are set false, we don't need to do anything. */
2175 if (!consider_outer_null && !consider_inner_null)
2176 return;
2177
2178 if (consider_outer_null && !consider_inner_null)
2179 {
2180 Assert(outer_has_null);
2181
2182 /*
2183 * If this is an outer join, the NULL partition on the outer side has
2184 * to be scanned all the way anyway; merge the NULL partition with a
2185 * dummy partition on the other side. In that case
2186 * consider_outer_null means that the NULL partition only contains
2187 * NULL values as the key values, so the merged partition will do so;
2188 * treat it as the NULL partition of the join relation.
2189 */
2190 if (IS_OUTER_JOIN(jointype))
2191 {
2192 Assert(jointype != JOIN_RIGHT);
2193 *null_index = merge_partition_with_dummy(outer_map, outer_null,
2194 next_index);
2195 }
2196 }
2197 else if (!consider_outer_null && consider_inner_null)
2198 {
2199 Assert(inner_has_null);
2200
2201 /*
2202 * If this is a FULL join, the NULL partition on the inner side has to
2203 * be scanned all the way anyway; merge the NULL partition with a
2204 * dummy partition on the other side. In that case
2205 * consider_inner_null means that the NULL partition only contains
2206 * NULL values as the key values, so the merged partition will do so;
2207 * treat it as the NULL partition of the join relation.
2208 */
2209 if (jointype == JOIN_FULL)
2210 *null_index = merge_partition_with_dummy(inner_map, inner_null,
2211 next_index);
2212 }
2213 else
2214 {
2215 Assert(consider_outer_null && consider_inner_null);
2216 Assert(outer_has_null);
2217 Assert(inner_has_null);
2218
2219 /*
2220 * If this is an outer join, the NULL partition on the outer side (and
2221 * that on the inner side if this is a FULL join) have to be scanned
2222 * all the way anyway, so merge them. Note that each of the NULL
2223 * partitions isn't merged yet, so they should be merged successfully.
2224 * Like the above, each of the NULL partitions only contains NULL
2225 * values as the key values, so the merged partition will do so; treat
2226 * it as the NULL partition of the join relation.
2227 *
2228 * Note: if this an INNER/SEMI join, the join clause will never be
2229 * satisfied by two NULL values (see comments above), so both the NULL
2230 * partitions can be eliminated.
2231 */
2232 if (IS_OUTER_JOIN(jointype))
2233 {
2234 Assert(jointype != JOIN_RIGHT);
2235 *null_index = merge_matching_partitions(outer_map, inner_map,
2236 outer_null, inner_null,
2237 next_index);
2238 Assert(*null_index >= 0);
2239 }
2240 }
2241}

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

◆ merge_partition_with_dummy()

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

Definition at line 2361 of file partbounds.c.

2362{
2363 int merged_index = *next_index;
2364
2365 Assert(index >= 0 && index < map->nparts);
2366 Assert(map->merged_indexes[index] == -1);
2367 Assert(!map->merged[index]);
2368 map->merged_indexes[index] = merged_index;
2369 /* Leave the merged flag alone! */
2370 *next_index = *next_index + 1;
2371 return merged_index;
2372}

References Assert(), PartitionMap::merged, and PartitionMap::merged_indexes.

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

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

1505{
1506 PartitionBoundInfo merged_bounds = NULL;
1507 PartitionBoundInfo outer_bi = outer_rel->boundinfo;
1508 PartitionBoundInfo inner_bi = inner_rel->boundinfo;
1509 bool outer_has_default = partition_bound_has_default(outer_bi);
1510 bool inner_has_default = partition_bound_has_default(inner_bi);
1511 int outer_default = outer_bi->default_index;
1512 int inner_default = inner_bi->default_index;
1513 PartitionMap outer_map;
1514 PartitionMap inner_map;
1515 int outer_index;
1516 int inner_index;
1517 int outer_lb_pos;
1518 int inner_lb_pos;
1519 PartitionRangeBound outer_lb;
1520 PartitionRangeBound outer_ub;
1521 PartitionRangeBound inner_lb;
1522 PartitionRangeBound inner_ub;
1523 int next_index = 0;
1524 int default_index = -1;
1525 List *merged_datums = NIL;
1526 List *merged_kinds = NIL;
1527 List *merged_indexes = NIL;
1528
1529 Assert(*outer_parts == NIL);
1530 Assert(*inner_parts == NIL);
1531 Assert(outer_bi->strategy == inner_bi->strategy &&
1532 outer_bi->strategy == PARTITION_STRATEGY_RANGE);
1533
1534 init_partition_map(outer_rel, &outer_map);
1535 init_partition_map(inner_rel, &inner_map);
1536
1537 /*
1538 * If the default partitions (if any) have been proven empty, deem them
1539 * non-existent.
1540 */
1541 if (outer_has_default && is_dummy_partition(outer_rel, outer_default))
1542 outer_has_default = false;
1543 if (inner_has_default && is_dummy_partition(inner_rel, inner_default))
1544 inner_has_default = false;
1545
1546 /*
1547 * Merge partitions from both sides. In each iteration we compare a pair
1548 * of ranges, one from each side, and decide whether the corresponding
1549 * partitions match or not. If the two ranges overlap, move to the next
1550 * pair of ranges, otherwise move to the next range on the side with a
1551 * lower range. outer_lb_pos/inner_lb_pos keep track of the positions of
1552 * lower bounds in the datums arrays in the outer/inner
1553 * PartitionBoundInfos respectively.
1554 */
1555 outer_lb_pos = inner_lb_pos = 0;
1556 outer_index = get_range_partition(outer_rel, outer_bi, &outer_lb_pos,
1557 &outer_lb, &outer_ub);
1558 inner_index = get_range_partition(inner_rel, inner_bi, &inner_lb_pos,
1559 &inner_lb, &inner_ub);
1560 while (outer_index >= 0 || inner_index >= 0)
1561 {
1562 bool overlap;
1563 int ub_cmpval;
1564 int lb_cmpval;
1565 PartitionRangeBound merged_lb = {-1, NULL, NULL, true};
1566 PartitionRangeBound merged_ub = {-1, NULL, NULL, false};
1567 int merged_index = -1;
1568
1569 /*
1570 * We run this loop till both sides finish. This allows us to avoid
1571 * duplicating code to handle the remaining ranges on the side which
1572 * finishes later. For that we set the comparison parameter cmpval in
1573 * such a way that it appears as if the side which finishes earlier
1574 * has an extra range higher than any other range on the unfinished
1575 * side. That way we advance the ranges on the unfinished side till
1576 * all of its ranges are exhausted.
1577 */
1578 if (outer_index == -1)
1579 {
1580 overlap = false;
1581 lb_cmpval = 1;
1582 ub_cmpval = 1;
1583 }
1584 else if (inner_index == -1)
1585 {
1586 overlap = false;
1587 lb_cmpval = -1;
1588 ub_cmpval = -1;
1589 }
1590 else
1591 overlap = compare_range_partitions(partnatts, partsupfuncs,
1592 partcollations,
1593 &outer_lb, &outer_ub,
1594 &inner_lb, &inner_ub,
1595 &lb_cmpval, &ub_cmpval);
1596
1597 if (overlap)
1598 {
1599 /* Two ranges overlap; form a join pair. */
1600
1601 PartitionRangeBound save_outer_ub;
1602 PartitionRangeBound save_inner_ub;
1603
1604 /* Both partitions should not have been merged yet. */
1605 Assert(outer_index >= 0);
1606 Assert(outer_map.merged_indexes[outer_index] == -1 &&
1607 outer_map.merged[outer_index] == false);
1608 Assert(inner_index >= 0);
1609 Assert(inner_map.merged_indexes[inner_index] == -1 &&
1610 inner_map.merged[inner_index] == false);
1611
1612 /*
1613 * Get the index of the merged partition. Both partitions aren't
1614 * merged yet, so the partitions should be merged successfully.
1615 */
1616 merged_index = merge_matching_partitions(&outer_map, &inner_map,
1617 outer_index, inner_index,
1618 &next_index);
1619 Assert(merged_index >= 0);
1620
1621 /* Get the range bounds of the merged partition. */
1622 get_merged_range_bounds(partnatts, partsupfuncs,
1623 partcollations, jointype,
1624 &outer_lb, &outer_ub,
1625 &inner_lb, &inner_ub,
1626 lb_cmpval, ub_cmpval,
1627 &merged_lb, &merged_ub);
1628
1629 /* Save the upper bounds of both partitions for use below. */
1630 save_outer_ub = outer_ub;
1631 save_inner_ub = inner_ub;
1632
1633 /* Move to the next pair of ranges. */
1634 outer_index = get_range_partition(outer_rel, outer_bi, &outer_lb_pos,
1635 &outer_lb, &outer_ub);
1636 inner_index = get_range_partition(inner_rel, inner_bi, &inner_lb_pos,
1637 &inner_lb, &inner_ub);
1638
1639 /*
1640 * If the range of a partition on one side overlaps the range of
1641 * the next partition on the other side, that will cause the
1642 * partition on one side to match at least two partitions on the
1643 * other side, which is the case that we currently don't support
1644 * partitioned join for; give up.
1645 */
1646 if (ub_cmpval > 0 && inner_index >= 0 &&
1647 compare_range_bounds(partnatts, partsupfuncs, partcollations,
1648 &save_outer_ub, &inner_lb) > 0)
1649 goto cleanup;
1650 if (ub_cmpval < 0 && outer_index >= 0 &&
1651 compare_range_bounds(partnatts, partsupfuncs, partcollations,
1652 &outer_lb, &save_inner_ub) < 0)
1653 goto cleanup;
1654
1655 /*
1656 * A row from a non-overlapping portion (if any) of a partition on
1657 * one side might find its join partner in the default partition
1658 * (if any) on the other side, causing the same situation as
1659 * above; give up in that case.
1660 */
1661 if ((outer_has_default && (lb_cmpval > 0 || ub_cmpval < 0)) ||
1662 (inner_has_default && (lb_cmpval < 0 || ub_cmpval > 0)))
1663 goto cleanup;
1664 }
1665 else if (ub_cmpval < 0)
1666 {
1667 /* A non-overlapping outer range. */
1668
1669 /* The outer partition should not have been merged yet. */
1670 Assert(outer_index >= 0);
1671 Assert(outer_map.merged_indexes[outer_index] == -1 &&
1672 outer_map.merged[outer_index] == false);
1673
1674 /*
1675 * If the inner side has the default partition, or this is an
1676 * outer join, try to assign a merged partition to the outer
1677 * partition (see process_outer_partition()). Otherwise, the
1678 * outer partition will not contribute to the result.
1679 */
1680 if (inner_has_default || IS_OUTER_JOIN(jointype))
1681 {
1682 merged_index = process_outer_partition(&outer_map,
1683 &inner_map,
1684 outer_has_default,
1685 inner_has_default,
1686 outer_index,
1687 inner_default,
1688 jointype,
1689 &next_index,
1690 &default_index);
1691 if (merged_index == -1)
1692 goto cleanup;
1693 merged_lb = outer_lb;
1694 merged_ub = outer_ub;
1695 }
1696
1697 /* Move to the next range on the outer side. */
1698 outer_index = get_range_partition(outer_rel, outer_bi, &outer_lb_pos,
1699 &outer_lb, &outer_ub);
1700 }
1701 else
1702 {
1703 /* A non-overlapping inner range. */
1704 Assert(ub_cmpval > 0);
1705
1706 /* The inner partition should not have been merged yet. */
1707 Assert(inner_index >= 0);
1708 Assert(inner_map.merged_indexes[inner_index] == -1 &&
1709 inner_map.merged[inner_index] == false);
1710
1711 /*
1712 * If the outer side has the default partition, or this is a FULL
1713 * join, try to assign a merged partition to the inner partition
1714 * (see process_inner_partition()). Otherwise, the inner
1715 * partition will not contribute to the result.
1716 */
1717 if (outer_has_default || jointype == JOIN_FULL)
1718 {
1719 merged_index = process_inner_partition(&outer_map,
1720 &inner_map,
1721 outer_has_default,
1722 inner_has_default,
1723 inner_index,
1724 outer_default,
1725 jointype,
1726 &next_index,
1727 &default_index);
1728 if (merged_index == -1)
1729 goto cleanup;
1730 merged_lb = inner_lb;
1731 merged_ub = inner_ub;
1732 }
1733
1734 /* Move to the next range on the inner side. */
1735 inner_index = get_range_partition(inner_rel, inner_bi, &inner_lb_pos,
1736 &inner_lb, &inner_ub);
1737 }
1738
1739 /*
1740 * If we assigned a merged partition, add the range bounds and index
1741 * of the merged partition if appropriate.
1742 */
1743 if (merged_index >= 0 && merged_index != default_index)
1744 add_merged_range_bounds(partnatts, partsupfuncs, partcollations,
1745 &merged_lb, &merged_ub, merged_index,
1746 &merged_datums, &merged_kinds,
1747 &merged_indexes);
1748 }
1749
1750 /* Merge the default partitions if any. */
1751 if (outer_has_default || inner_has_default)
1752 merge_default_partitions(&outer_map, &inner_map,
1753 outer_has_default, inner_has_default,
1754 outer_default, inner_default,
1755 jointype, &next_index, &default_index);
1756 else
1757 Assert(default_index == -1);
1758
1759 /* If we have merged partitions, create the partition bounds. */
1760 if (next_index > 0)
1761 {
1762 /*
1763 * Unlike the case of list partitioning, we wouldn't have re-merged
1764 * partitions, so did_remapping should be left alone.
1765 */
1766 Assert(!outer_map.did_remapping);
1767 Assert(!inner_map.did_remapping);
1768
1769 /* Use maps to match partitions from inputs. */
1770 generate_matching_part_pairs(outer_rel, inner_rel,
1771 &outer_map, &inner_map,
1772 next_index,
1773 outer_parts, inner_parts);
1774 Assert(*outer_parts != NIL);
1775 Assert(*inner_parts != NIL);
1776 Assert(list_length(*outer_parts) == list_length(*inner_parts));
1777 Assert(list_length(*outer_parts) == next_index);
1778
1779 /* Make a PartitionBoundInfo struct to return. */
1780 merged_bounds = build_merged_partition_bounds(outer_bi->strategy,
1781 merged_datums,
1782 merged_kinds,
1783 merged_indexes,
1784 -1,
1785 default_index);
1786 Assert(merged_bounds);
1787 }
1788
1789cleanup:
1790 /* Free local memory before returning. */
1791 list_free(merged_datums);
1792 list_free(merged_kinds);
1793 list_free(merged_indexes);
1794 free_partition_map(&outer_map);
1795 free_partition_map(&inner_map);
1796
1797 return merged_bounds;
1798}
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:2704
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:2768
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:2655
static int get_range_partition(RelOptInfo *rel, PartitionBoundInfo bi, int *lb_pos, PartitionRangeBound *lb, PartitionRangeBound *ub)
Definition: partbounds.c:2574

References add_merged_range_bounds(), Assert(), 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().

◆ partition_bounds_copy()

PartitionBoundInfo partition_bounds_copy ( PartitionBoundInfo  src,
PartitionKey  key 
)

Definition at line 995 of file partbounds.c.

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

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

Referenced by RelationBuildPartitionDesc().

◆ partition_bounds_create()

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

Definition at line 300 of file partbounds.c.

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

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

Referenced by RelationBuildPartitionDesc().

◆ partition_bounds_equal()

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

Definition at line 889 of file partbounds.c.

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

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

Referenced by compute_partition_bounds().

◆ partition_bounds_merge()

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

Definition at line 1112 of file partbounds.c.

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

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

Referenced by compute_partition_bounds().

◆ partition_hash_bsearch()

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

Definition at line 3730 of file partbounds.c.

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

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

Referenced by check_new_partition_bound().

◆ partition_hbound_cmp()

static int32 partition_hbound_cmp ( int  modulus1,
int  remainder1,
int  modulus2,
int  remainder2 
)
static

Definition at line 3579 of file partbounds.c.

3580{
3581 if (modulus1 < modulus2)
3582 return -1;
3583 if (modulus1 > modulus2)
3584 return 1;
3585 if (modulus1 == modulus2 && remainder1 != remainder2)
3586 return (remainder1 > remainder2) ? 1 : -1;
3587 return 0;
3588}

Referenced by partition_hash_bsearch(), and qsort_partition_hbound_cmp().

◆ partition_list_bsearch()

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

Definition at line 3599 of file partbounds.c.

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

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

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

◆ partition_range_bsearch()

static int partition_range_bsearch ( int  partnatts,
FmgrInfo partsupfunc,
Oid partcollation,
PartitionBoundInfo  boundinfo,
PartitionRangeBound probe,
int32 cmpval 
)
static

Definition at line 3645 of file partbounds.c.

3649{
3650 int lo,
3651 hi,
3652 mid;
3653
3654 lo = -1;
3655 hi = boundinfo->ndatums - 1;
3656 while (lo < hi)
3657 {
3658 mid = (lo + hi + 1) / 2;
3659 *cmpval = partition_rbound_cmp(partnatts, partsupfunc,
3660 partcollation,
3661 boundinfo->datums[mid],
3662 boundinfo->kind[mid],
3663 (boundinfo->indexes[mid] == -1),
3664 probe);
3665 if (*cmpval <= 0)
3666 {
3667 lo = mid;
3668 if (*cmpval == 0)
3669 break;
3670 }
3671 else
3672 hi = mid - 1;
3673 }
3674
3675 return lo;
3676}

References PartitionBoundInfoData::datums, PartitionBoundInfoData::indexes, PartitionBoundInfoData::kind, PartitionBoundInfoData::ndatums, and partition_rbound_cmp().

Referenced by check_new_partition_bound().

◆ partition_range_datum_bsearch()

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

Definition at line 3687 of file partbounds.c.

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

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

Referenced by get_matching_range_bounds(), and get_partition_for_tuple().

◆ partition_rbound_cmp()

static int32 partition_rbound_cmp ( int  partnatts,
FmgrInfo partsupfunc,
Oid partcollation,
Datum datums1,
PartitionRangeDatumKind kind1,
bool  lower1,
PartitionRangeBound b2 
)
static

Definition at line 3480 of file partbounds.c.

3484{
3485 int32 colnum = 0;
3486 int32 cmpval = 0; /* placate compiler */
3487 int i;
3488 Datum *datums2 = b2->datums;
3489 PartitionRangeDatumKind *kind2 = b2->kind;
3490 bool lower2 = b2->lower;
3491
3492 for (i = 0; i < partnatts; i++)
3493 {
3494 /* Track column number in case we need it for result */
3495 colnum++;
3496
3497 /*
3498 * First, handle cases where the column is unbounded, which should not
3499 * invoke the comparison procedure, and should not consider any later
3500 * columns. Note that the PartitionRangeDatumKind enum elements
3501 * compare the same way as the values they represent.
3502 */
3503 if (kind1[i] < kind2[i])
3504 return -colnum;
3505 else if (kind1[i] > kind2[i])
3506 return colnum;
3507 else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
3508 {
3509 /*
3510 * The column bounds are both MINVALUE or both MAXVALUE. No later
3511 * columns should be considered, but we still need to compare
3512 * whether they are upper or lower bounds.
3513 */
3514 break;
3515 }
3516
3517 cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
3518 partcollation[i],
3519 datums1[i],
3520 datums2[i]));
3521 if (cmpval != 0)
3522 break;
3523 }
3524
3525 /*
3526 * If the comparison is anything other than equal, we're done. If they
3527 * compare equal though, we still have to consider whether the boundaries
3528 * are inclusive or exclusive. Exclusive one is considered smaller of the
3529 * two.
3530 */
3531 if (cmpval == 0 && lower1 != lower2)
3532 cmpval = lower1 ? 1 : -1;
3533
3534 return cmpval == 0 ? 0 : (cmpval < 0 ? -colnum : colnum);
3535}

References DatumGetInt32(), PartitionRangeBound::datums, FunctionCall2Coll(), i, PartitionRangeBound::kind, PartitionRangeBound::lower, and PARTITION_RANGE_DATUM_VALUE.

Referenced by add_merged_range_bounds(), check_new_partition_bound(), check_partition_bounds_for_split_range(), check_two_partitions_bounds_range(), and partition_range_bsearch().

◆ partition_rbound_datum_cmp()

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

Definition at line 3548 of file partbounds.c.

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

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

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

◆ partitions_are_ordered()

bool partitions_are_ordered ( PartitionBoundInfo  boundinfo,
Bitmapset live_parts 
)

Definition at line 2845 of file partbounds.c.

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

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

Referenced by build_partition_pathkeys(), and generate_orderedappend_paths().

◆ partitions_listdatum_intersection()

static List * partitions_listdatum_intersection ( FmgrInfo partsupfunc,
Oid partcollation,
const List list1,
const List list2 
)
static

Definition at line 5204 of file partbounds.c.

5206{
5207 List *result = NIL;
5208
5209 if (list1 == NIL || list2 == NIL)
5210 return result;
5211
5212 foreach_node(Const, val1, list1)
5213 {
5214 bool isnull1 = val1->constisnull;
5215
5216 foreach_node(Const, val2, list2)
5217 {
5218 if (val2->constisnull)
5219 {
5220 if (isnull1)
5221 {
5222 result = lappend(result, val1);
5223 return result;
5224 }
5225 continue;
5226 }
5227 else if (isnull1)
5228 continue;
5229
5230 /* Compare two datum values. */
5231 if (DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
5232 partcollation[0],
5233 val1->constvalue,
5234 val2->constvalue)) == 0)
5235 {
5236 result = lappend(result, val1);
5237 return result;
5238 }
5239 }
5240 }
5241
5242 return result;
5243}

References DatumGetInt32(), foreach_node, FunctionCall2Coll(), lappend(), and NIL.

Referenced by check_partitions_not_overlap_list().

◆ process_inner_partition()

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

Definition at line 2056 of file partbounds.c.

2065{
2066 int merged_index = -1;
2067
2068 Assert(inner_index >= 0);
2069
2070 /*
2071 * If the outer side has the default partition, a row from the inner
2072 * partition might find its join partner in the default partition; try
2073 * merging the inner partition with the default partition. Otherwise,
2074 * this should be a FULL join, in which case the inner partition has to be
2075 * scanned all the way anyway; merge the inner partition with a dummy
2076 * partition on the other side.
2077 */
2078 if (outer_has_default)
2079 {
2080 Assert(outer_default >= 0);
2081
2082 /*
2083 * If the inner side has the default partition as well, the default
2084 * partition on the outer side will have two matching partitions on
2085 * the other side: the inner partition and the default partition on
2086 * the inner side. Partitionwise join doesn't handle this scenario
2087 * yet.
2088 */
2089 if (inner_has_default)
2090 return -1;
2091
2092 merged_index = merge_matching_partitions(outer_map, inner_map,
2093 outer_default, inner_index,
2094 next_index);
2095 if (merged_index == -1)
2096 return -1;
2097
2098 /*
2099 * If this is an outer join, the default partition on the outer side
2100 * has to be scanned all the way anyway, so the resulting partition
2101 * will contain all key values from the default partition, which any
2102 * other partition of the join relation will not contain. Thus the
2103 * resulting partition will act as the default partition of the join
2104 * relation; record the index in *default_index if not already done.
2105 */
2106 if (IS_OUTER_JOIN(jointype))
2107 {
2108 Assert(jointype != JOIN_RIGHT);
2109 if (*default_index == -1)
2110 *default_index = merged_index;
2111 else
2112 Assert(*default_index == merged_index);
2113 }
2114 }
2115 else
2116 {
2117 Assert(jointype == JOIN_FULL);
2118
2119 /* If we have already assigned a partition, no need to do anything. */
2120 merged_index = inner_map->merged_indexes[inner_index];
2121 if (merged_index == -1)
2122 merged_index = merge_partition_with_dummy(inner_map, inner_index,
2123 next_index);
2124 }
2125 return merged_index;
2126}

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

◆ process_outer_partition()

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

Definition at line 1974 of file partbounds.c.

1983{
1984 int merged_index = -1;
1985
1986 Assert(outer_index >= 0);
1987
1988 /*
1989 * If the inner side has the default partition, a row from the outer
1990 * partition might find its join partner in the default partition; try
1991 * merging the outer partition with the default partition. Otherwise,
1992 * this should be an outer join, in which case the outer partition has to
1993 * be scanned all the way anyway; merge the outer partition with a dummy
1994 * partition on the other side.
1995 */
1996 if (inner_has_default)
1997 {
1998 Assert(inner_default >= 0);
1999
2000 /*
2001 * If the outer side has the default partition as well, the default
2002 * partition on the inner side will have two matching partitions on
2003 * the other side: the outer partition and the default partition on
2004 * the outer side. Partitionwise join doesn't handle this scenario
2005 * yet.
2006 */
2007 if (outer_has_default)
2008 return -1;
2009
2010 merged_index = merge_matching_partitions(outer_map, inner_map,
2011 outer_index, inner_default,
2012 next_index);
2013 if (merged_index == -1)
2014 return -1;
2015
2016 /*
2017 * If this is a FULL join, the default partition on the inner side has
2018 * to be scanned all the way anyway, so the resulting partition will
2019 * contain all key values from the default partition, which any other
2020 * partition of the join relation will not contain. Thus the
2021 * resulting partition will act as the default partition of the join
2022 * relation; record the index in *default_index if not already done.
2023 */
2024 if (jointype == JOIN_FULL)
2025 {
2026 if (*default_index == -1)
2027 *default_index = merged_index;
2028 else
2029 Assert(*default_index == merged_index);
2030 }
2031 }
2032 else
2033 {
2034 Assert(IS_OUTER_JOIN(jointype));
2035 Assert(jointype != JOIN_RIGHT);
2036
2037 /* If we have already assigned a partition, no need to do anything. */
2038 merged_index = outer_map->merged_indexes[outer_index];
2039 if (merged_index == -1)
2040 merged_index = merge_partition_with_dummy(outer_map, outer_index,
2041 next_index);
2042 }
2043 return merged_index;
2044}

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

◆ qsort_partition_hbound_cmp()

static int32 qsort_partition_hbound_cmp ( const void *  a,
const void *  b 
)
static

Definition at line 3770 of file partbounds.c.

3771{
3772 const PartitionHashBound *h1 = (const PartitionHashBound *) a;
3773 const PartitionHashBound *h2 = (const PartitionHashBound *) b;
3774
3775 return partition_hbound_cmp(h1->modulus, h1->remainder,
3776 h2->modulus, h2->remainder);
3777}
int b
Definition: isn.c:74
int a
Definition: isn.c:73

References a, b, PartitionHashBound::modulus, partition_hbound_cmp(), and PartitionHashBound::remainder.

Referenced by create_hash_bounds().

◆ qsort_partition_list_value_cmp()

static int32 qsort_partition_list_value_cmp ( const void *  a,
const void *  b,
void *  arg 
)
static

Definition at line 3785 of file partbounds.c.

3786{
3787 Datum val1 = ((const PartitionListValue *) a)->value,
3788 val2 = ((const PartitionListValue *) b)->value;
3790
3791 return DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
3792 key->partcollation[0],
3793 val1, val2));
3794}
struct PartitionKeyData * PartitionKey
Definition: partdefs.h:18
void * arg

References a, arg, b, DatumGetInt32(), FunctionCall2Coll(), and sort-test::key.

Referenced by create_list_bounds().

◆ qsort_partition_rbound_cmp()

static int32 qsort_partition_rbound_cmp ( const void *  a,
const void *  b,
void *  arg 
)
static

Definition at line 3802 of file partbounds.c.

3803{
3804 PartitionRangeBound *b1 = (*(PartitionRangeBound *const *) a);
3805 PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
3807
3808 return compare_range_bounds(key->partnatts, key->partsupfunc,
3809 key->partcollation,
3810 b1, b2);
3811}

References a, arg, b, compare_range_bounds, and sort-test::key.

Referenced by calculate_partition_bound_for_merge(), check_partitions_for_split(), and create_range_bounds().

◆ satisfies_hash_partition()

Datum satisfies_hash_partition ( PG_FUNCTION_ARGS  )

Definition at line 4763 of file partbounds.c.

4764{
4765 typedef struct ColumnsHashData
4766 {
4767 Oid relid;
4768 int nkeys;
4769 Oid variadic_type;
4770 int16 variadic_typlen;
4771 bool variadic_typbyval;
4772 char variadic_typalign;
4773 Oid partcollid[PARTITION_MAX_KEYS];
4774 FmgrInfo partsupfunc[FLEXIBLE_ARRAY_MEMBER];
4775 } ColumnsHashData;
4776 Oid parentId;
4777 int modulus;
4778 int remainder;
4780 ColumnsHashData *my_extra;
4781 uint64 rowHash = 0;
4782
4783 /* Return false if the parent OID, modulus, or remainder is NULL. */
4784 if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2))
4785 PG_RETURN_BOOL(false);
4786 parentId = PG_GETARG_OID(0);
4787 modulus = PG_GETARG_INT32(1);
4789
4790 /* Sanity check modulus and remainder. */
4791 if (modulus <= 0)
4792 ereport(ERROR,
4793 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4794 errmsg("modulus for hash partition must be an integer value greater than zero")));
4795 if (remainder < 0)
4796 ereport(ERROR,
4797 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4798 errmsg("remainder for hash partition must be an integer value greater than or equal to zero")));
4799 if (remainder >= modulus)
4800 ereport(ERROR,
4801 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4802 errmsg("remainder for hash partition must be less than modulus")));
4803
4804 /*
4805 * Cache hash function information.
4806 */
4807 my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
4808 if (my_extra == NULL || my_extra->relid != parentId)
4809 {
4810 Relation parent;
4812 int j;
4813
4814 /* Open parent relation and fetch partition key info */
4815 parent = relation_open(parentId, AccessShareLock);
4816 key = RelationGetPartitionKey(parent);
4817
4818 /* Reject parent table that is not hash-partitioned. */
4819 if (key == NULL || key->strategy != PARTITION_STRATEGY_HASH)
4820 ereport(ERROR,
4821 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4822 errmsg("\"%s\" is not a hash partitioned table",
4823 get_rel_name(parentId))));
4824
4825 if (!get_fn_expr_variadic(fcinfo->flinfo))
4826 {
4827 int nargs = PG_NARGS() - 3;
4828
4829 /* complain if wrong number of column values */
4830 if (key->partnatts != nargs)
4831 ereport(ERROR,
4832 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4833 errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
4834 key->partnatts, nargs)));
4835
4836 /* allocate space for our cache */
4837 fcinfo->flinfo->fn_extra =
4838 MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
4839 offsetof(ColumnsHashData, partsupfunc) +
4840 sizeof(FmgrInfo) * nargs);
4841 my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
4842 my_extra->relid = parentId;
4843 my_extra->nkeys = key->partnatts;
4844 memcpy(my_extra->partcollid, key->partcollation,
4845 key->partnatts * sizeof(Oid));
4846
4847 /* check argument types and save fmgr_infos */
4848 for (j = 0; j < key->partnatts; ++j)
4849 {
4850 Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, j + 3);
4851
4852 if (argtype != key->parttypid[j] && !IsBinaryCoercible(argtype, key->parttypid[j]))
4853 ereport(ERROR,
4854 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4855 errmsg("column %d of the partition key has type %s, but supplied value is of type %s",
4856 j + 1, format_type_be(key->parttypid[j]), format_type_be(argtype))));
4857
4858 fmgr_info_copy(&my_extra->partsupfunc[j],
4859 &key->partsupfunc[j],
4860 fcinfo->flinfo->fn_mcxt);
4861 }
4862 }
4863 else
4864 {
4865 ArrayType *variadic_array = PG_GETARG_ARRAYTYPE_P(3);
4866
4867 /* allocate space for our cache -- just one FmgrInfo in this case */
4868 fcinfo->flinfo->fn_extra =
4869 MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
4870 offsetof(ColumnsHashData, partsupfunc) +
4871 sizeof(FmgrInfo));
4872 my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
4873 my_extra->relid = parentId;
4874 my_extra->nkeys = key->partnatts;
4875 my_extra->variadic_type = ARR_ELEMTYPE(variadic_array);
4876 get_typlenbyvalalign(my_extra->variadic_type,
4877 &my_extra->variadic_typlen,
4878 &my_extra->variadic_typbyval,
4879 &my_extra->variadic_typalign);
4880 my_extra->partcollid[0] = key->partcollation[0];
4881
4882 /* check argument types */
4883 for (j = 0; j < key->partnatts; ++j)
4884 if (key->parttypid[j] != my_extra->variadic_type)
4885 ereport(ERROR,
4886 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4887 errmsg("column %d of the partition key has type \"%s\", but supplied value is of type \"%s\"",
4888 j + 1,
4889 format_type_be(key->parttypid[j]),
4890 format_type_be(my_extra->variadic_type))));
4891
4892 fmgr_info_copy(&my_extra->partsupfunc[0],
4893 &key->partsupfunc[0],
4894 fcinfo->flinfo->fn_mcxt);
4895 }
4896
4897 /* Hold lock until commit */
4898 relation_close(parent, NoLock);
4899 }
4900
4901 if (!OidIsValid(my_extra->variadic_type))
4902 {
4903 int nkeys = my_extra->nkeys;
4904 int i;
4905
4906 /*
4907 * For a non-variadic call, neither the number of arguments nor their
4908 * types can change across calls, so avoid the expense of rechecking
4909 * here.
4910 */
4911
4912 for (i = 0; i < nkeys; i++)
4913 {
4914 Datum hash;
4915
4916 /* keys start from fourth argument of function. */
4917 int argno = i + 3;
4918
4919 if (PG_ARGISNULL(argno))
4920 continue;
4921
4922 hash = FunctionCall2Coll(&my_extra->partsupfunc[i],
4923 my_extra->partcollid[i],
4924 PG_GETARG_DATUM(argno),
4925 seed);
4926
4927 /* Form a single 64-bit hash value */
4928 rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
4929 }
4930 }
4931 else
4932 {
4933 ArrayType *variadic_array = PG_GETARG_ARRAYTYPE_P(3);
4934 int i;
4935 int nelems;
4936 Datum *datum;
4937 bool *isnull;
4938
4939 deconstruct_array(variadic_array,
4940 my_extra->variadic_type,
4941 my_extra->variadic_typlen,
4942 my_extra->variadic_typbyval,
4943 my_extra->variadic_typalign,
4944 &datum, &isnull, &nelems);
4945
4946 /* complain if wrong number of column values */
4947 if (nelems != my_extra->nkeys)
4948 ereport(ERROR,
4949 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4950 errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
4951 my_extra->nkeys, nelems)));
4952
4953 for (i = 0; i < nelems; i++)
4954 {
4955 Datum hash;
4956
4957 if (isnull[i])
4958 continue;
4959
4960 hash = FunctionCall2Coll(&my_extra->partsupfunc[0],
4961 my_extra->partcollid[0],
4962 datum[i],
4963 seed);
4964
4965 /* Form a single 64-bit hash value */
4966 rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
4967 }
4968 }
4969
4970 PG_RETURN_BOOL(rowHash % modulus == remainder);
4971}
#define PG_GETARG_ARRAYTYPE_P(n)
Definition: array.h:263
#define ARR_ELEMTYPE(a)
Definition: array.h:292
void deconstruct_array(const ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3632
#define FLEXIBLE_ARRAY_MEMBER
Definition: c.h:486
int16_t int16
Definition: c.h:547
bool get_fn_expr_variadic(FmgrInfo *flinfo)
Definition: fmgr.c:2009
Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)
Definition: fmgr.c:1875
void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt)
Definition: fmgr.c:581
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_ARGISNULL(n)
Definition: fmgr.h:209
#define PG_GETARG_DATUM(n)
Definition: fmgr.h:268
#define PG_NARGS()
Definition: fmgr.h:203
#define PG_GETARG_INT32(n)
Definition: fmgr.h:269
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
#define AccessShareLock
Definition: lockdefs.h:36
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition: lsyscache.c:2436
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1263
bool IsBinaryCoercible(Oid srctype, Oid targettype)
#define PARTITION_MAX_KEYS
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:47
Definition: fmgr.h:57

References AccessShareLock, ARR_ELEMTYPE, DatumGetUInt64(), deconstruct_array(), ereport, errcode(), errmsg(), ERROR, FLEXIBLE_ARRAY_MEMBER, fmgr_info_copy(), format_type_be(), FunctionCall2Coll(), get_fn_expr_argtype(), get_fn_expr_variadic(), get_rel_name(), get_typlenbyvalalign(), hash(), hash_combine64(), HASH_PARTITION_SEED, i, if(), IsBinaryCoercible(), j, sort-test::key, MemoryContextAllocZero(), NoLock, OidIsValid, PARTITION_MAX_KEYS, PARTITION_STRATEGY_HASH, PG_ARGISNULL, PG_GETARG_ARRAYTYPE_P, PG_GETARG_DATUM, PG_GETARG_INT32, PG_GETARG_OID, PG_NARGS, PG_RETURN_BOOL, relation_close(), relation_open(), RelationGetPartitionKey(), remainder, and UInt64GetDatum().