PostgreSQL Source Code git master
partbounds.c File Reference
#include "postgres.h"
#include "access/relation.h"
#include "access/table.h"
#include "access/tableam.h"
#include "catalog/partition.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_type.h"
#include "commands/tablecmds.h"
#include "common/hashfn.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/pathnodes.h"
#include "parser/parse_coerce.h"
#include "partitioning/partbounds.h"
#include "partitioning/partdesc.h"
#include "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, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *tuple_datums, int n_tuple_datums)
 
int partition_list_bsearch (FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
 
int partition_range_datum_bsearch (FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, int nvalues, Datum *values, bool *is_equal)
 
int partition_hash_bsearch (PartitionBoundInfo boundinfo, int modulus, int remainder)
 
uint64 compute_partition_hash_value (int partnatts, FmgrInfo *partsupfunc, const Oid *partcollation, const Datum *values, const bool *isnull)
 
Datum satisfies_hash_partition (PG_FUNCTION_ARGS)
 

Macro Definition Documentation

◆ compare_range_bounds

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

Definition at line 88 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 2775 of file partbounds.c.

2783{
2784 int cmpval;
2785
2786 if (!*merged_datums)
2787 {
2788 /* First merged partition */
2789 Assert(!*merged_kinds);
2790 Assert(!*merged_indexes);
2791 cmpval = 1;
2792 }
2793 else
2794 {
2795 PartitionRangeBound prev_ub;
2796
2797 Assert(*merged_datums);
2798 Assert(*merged_kinds);
2799 Assert(*merged_indexes);
2800
2801 /* Get the last upper bound. */
2802 prev_ub.index = llast_int(*merged_indexes);
2803 prev_ub.datums = (Datum *) llast(*merged_datums);
2804 prev_ub.kind = (PartitionRangeDatumKind *) llast(*merged_kinds);
2805 prev_ub.lower = false;
2806
2807 /*
2808 * We pass lower1 = false to partition_rbound_cmp() to prevent it from
2809 * considering the last upper bound to be smaller than the lower bound
2810 * of the merged partition when the values of the two range bounds
2811 * compare equal.
2812 */
2813 cmpval = partition_rbound_cmp(partnatts, partsupfuncs, partcollations,
2814 merged_lb->datums, merged_lb->kind,
2815 false, &prev_ub);
2816 Assert(cmpval >= 0);
2817 }
2818
2819 /*
2820 * If the lower bound is higher than the last upper bound, add the lower
2821 * bound with the index as -1 indicating that that is a lower bound; else,
2822 * the last upper bound will be reused as the lower bound of the merged
2823 * partition, so skip this.
2824 */
2825 if (cmpval > 0)
2826 {
2827 *merged_datums = lappend(*merged_datums, merged_lb->datums);
2828 *merged_kinds = lappend(*merged_kinds, merged_lb->kind);
2829 *merged_indexes = lappend_int(*merged_indexes, -1);
2830 }
2831
2832 /* Add the upper bound and index of the merged partition. */
2833 *merged_datums = lappend(*merged_datums, merged_ub->datums);
2834 *merged_kinds = lappend(*merged_kinds, merged_ub->kind);
2835 *merged_indexes = lappend_int(*merged_indexes, merged_index);
2836}
#define Assert(condition)
Definition: c.h:815
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:934
#define llast_int(l)
Definition: pg_list.h:199
#define llast(l)
Definition: pg_list.h:198
uintptr_t Datum
Definition: postgres.h:69
PartitionRangeDatumKind * kind
Definition: partbounds.c:68

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

2521{
2522 PartitionBoundInfo merged_bounds;
2523 int ndatums = list_length(merged_datums);
2524 int pos;
2525 ListCell *lc;
2526
2527 merged_bounds = (PartitionBoundInfo) palloc(sizeof(PartitionBoundInfoData));
2528 merged_bounds->strategy = strategy;
2529 merged_bounds->ndatums = ndatums;
2530
2531 merged_bounds->datums = (Datum **) palloc(sizeof(Datum *) * ndatums);
2532 pos = 0;
2533 foreach(lc, merged_datums)
2534 merged_bounds->datums[pos++] = (Datum *) lfirst(lc);
2535
2536 if (strategy == PARTITION_STRATEGY_RANGE)
2537 {
2538 Assert(list_length(merged_kinds) == ndatums);
2539 merged_bounds->kind = (PartitionRangeDatumKind **)
2540 palloc(sizeof(PartitionRangeDatumKind *) * ndatums);
2541 pos = 0;
2542 foreach(lc, merged_kinds)
2543 merged_bounds->kind[pos++] = (PartitionRangeDatumKind *) lfirst(lc);
2544
2545 /* There are ndatums+1 indexes in the case of range partitioning. */
2546 merged_indexes = lappend_int(merged_indexes, -1);
2547 ndatums++;
2548 }
2549 else
2550 {
2551 Assert(strategy == PARTITION_STRATEGY_LIST);
2552 Assert(merged_kinds == NIL);
2553 merged_bounds->kind = NULL;
2554 }
2555
2556 /* interleaved_parts is always NULL for join relations. */
2557 merged_bounds->interleaved_parts = NULL;
2558
2559 Assert(list_length(merged_indexes) == ndatums);
2560 merged_bounds->nindexes = ndatums;
2561 merged_bounds->indexes = (int *) palloc(sizeof(int) * ndatums);
2562 pos = 0;
2563 foreach(lc, merged_indexes)
2564 merged_bounds->indexes[pos++] = lfirst_int(lc);
2565
2566 merged_bounds->null_index = null_index;
2567 merged_bounds->default_index = default_index;
2568
2569 return merged_bounds;
2570}
void * palloc(Size size)
Definition: mcxt.c:1317
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:883
@ PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:884
struct PartitionBoundInfoData * PartitionBoundInfo
Definition: partdefs.h:16
#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(), PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, and PartitionBoundInfoData::strategy.

Referenced by merge_list_bounds(), and merge_range_bounds().

◆ check_default_partition_contents()

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

Definition at line 3251 of file partbounds.c.

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

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

Referenced by DefineRelation().

◆ check_new_partition_bound()

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

Definition at line 2896 of file partbounds.c.

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

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

Referenced by ATExecAttachPartition(), and DefineRelation().

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

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

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

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

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

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(), PARTITION_STRATEGY_HASH, pfree(), qsort, qsort_partition_hbound_cmp(), PartitionHashBound::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 462 of file partbounds.c.

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

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(), 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 677 of file partbounds.c.

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

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

◆ fix_merged_indexes()

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

Definition at line 2385 of file partbounds.c.

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

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

Referenced by merge_list_bounds().

◆ free_partition_map()

static void free_partition_map ( PartitionMap map)
static

Definition at line 1832 of file partbounds.c.

1833{
1834 pfree(map->merged_indexes);
1835 pfree(map->merged);
1836 pfree(map->old_indexes);
1837}
bool * merged
Definition: partbounds.c:80

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

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

References Assert, i, lappend(), Max, PartitionMap::merged_indexes, NIL, PartitionMap::nparts, RelOptInfo::nparts, palloc(), 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 3414 of file partbounds.c.

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

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

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

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

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

References i, lfirst_node, and val.

Referenced by create_list_bounds().

◆ get_partition_operator()

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

Definition at line 3832 of file partbounds.c.

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

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

3984{
3986 FuncExpr *fexpr;
3987 Node *relidConst;
3988 Node *modulusConst;
3989 Node *remainderConst;
3990 List *args;
3991 ListCell *partexprs_item;
3992 int i;
3993
3994 /* Fixed arguments. */
3995 relidConst = (Node *) makeConst(OIDOID,
3996 -1,
3997 InvalidOid,
3998 sizeof(Oid),
4000 false,
4001 true);
4002
4003 modulusConst = (Node *) makeConst(INT4OID,
4004 -1,
4005 InvalidOid,
4006 sizeof(int32),
4007 Int32GetDatum(spec->modulus),
4008 false,
4009 true);
4010
4011 remainderConst = (Node *) makeConst(INT4OID,
4012 -1,
4013 InvalidOid,
4014 sizeof(int32),
4015 Int32GetDatum(spec->remainder),
4016 false,
4017 true);
4018
4019 args = list_make3(relidConst, modulusConst, remainderConst);
4020 partexprs_item = list_head(key->partexprs);
4021
4022 /* Add an argument for each key column. */
4023 for (i = 0; i < key->partnatts; i++)
4024 {
4025 Node *keyCol;
4026
4027 /* Left operand */
4028 if (key->partattrs[i] != 0)
4029 {
4030 keyCol = (Node *) makeVar(1,
4031 key->partattrs[i],
4032 key->parttypid[i],
4033 key->parttypmod[i],
4034 key->parttypcoll[i],
4035 0);
4036 }
4037 else
4038 {
4039 keyCol = (Node *) copyObject(lfirst(partexprs_item));
4040 partexprs_item = lnext(key->partexprs, partexprs_item);
4041 }
4042
4043 args = lappend(args, keyCol);
4044 }
4045
4046 fexpr = makeFuncExpr(F_SATISFIES_HASH_PARTITION,
4047 BOOLOID,
4048 args,
4049 InvalidOid,
4050 InvalidOid,
4052
4053 return list_make1(fexpr);
4054}
int32_t int32
Definition: c.h:484
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:547
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:303
#define copyObject(obj)
Definition: nodes.h:224
#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:257
#define InvalidOid
Definition: postgres_ext.h:37
@ COERCE_EXPLICIT_CALL
Definition: primnodes.h:750
Definition: nodes.h:129

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

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

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

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

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

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

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(), and generate_partition_qual().

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

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

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

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

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

2586{
2587 int part_index;
2588
2590
2591 do
2592 {
2593 part_index = get_range_partition_internal(bi, lb_pos, lb, ub);
2594 if (part_index == -1)
2595 return -1;
2596 } while (is_dummy_partition(rel, part_index));
2597
2598 return part_index;
2599}
static bool is_dummy_partition(RelOptInfo *rel, int part_index)
Definition: partbounds.c:1843
static int get_range_partition_internal(PartitionBoundInfo bi, int *lb_pos, PartitionRangeBound *lb, PartitionRangeBound *ub)
Definition: partbounds.c:2602

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

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

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

1812{
1813 int nparts = rel->nparts;
1814 int i;
1815
1816 map->nparts = nparts;
1817 map->merged_indexes = (int *) palloc(sizeof(int) * nparts);
1818 map->merged = (bool *) palloc(sizeof(bool) * nparts);
1819 map->did_remapping = false;
1820 map->old_indexes = (int *) palloc(sizeof(int) * nparts);
1821 for (i = 0; i < nparts; i++)
1822 {
1823 map->merged_indexes[i] = map->old_indexes[i] = -1;
1824 map->merged[i] = false;
1825 }
1826}

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

Referenced by merge_list_bounds(), and merge_range_bounds().

◆ is_dummy_partition()

static bool is_dummy_partition ( RelOptInfo rel,
int  part_index 
)
static

Definition at line 1843 of file partbounds.c.

1844{
1845 RelOptInfo *part_rel;
1846
1847 Assert(part_index >= 0);
1848 part_rel = rel->part_rels[part_index];
1849 if (part_rel == NULL || IS_DUMMY_REL(part_rel))
1850 return true;
1851 return false;
1852}
#define IS_DUMMY_REL(r)
Definition: pathnodes.h:1958

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

3429{
3430 PartitionRangeBound *bound;
3431 ListCell *lc;
3432 int i;
3433
3434 Assert(datums != NIL);
3435
3436 bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
3437 bound->index = index;
3438 bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
3439 bound->kind = (PartitionRangeDatumKind *) palloc0(key->partnatts *
3440 sizeof(PartitionRangeDatumKind));
3441 bound->lower = lower;
3442
3443 i = 0;
3444 foreach(lc, datums)
3445 {
3447
3448 /* What's contained in this range datum? */
3449 bound->kind[i] = datum->kind;
3450
3451 if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
3452 {
3453 Const *val = castNode(Const, datum->value);
3454
3455 if (val->constisnull)
3456 elog(ERROR, "invalid range bound datum");
3457 bound->datums[i] = val->constvalue;
3458 }
3459
3460 i++;
3461 }
3462
3463 return bound;
3464}

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

Referenced by check_new_partition_bound(), 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 3868 of file partbounds.c.

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

References ScalarArrayOpExpr::args, Assert, COERCE_EXPLICIT_CAST, ArrayExpr::elements, 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 2257 of file partbounds.c.

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

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

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

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

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

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

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

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

2368{
2369 int merged_index = *next_index;
2370
2371 Assert(index >= 0 && index < map->nparts);
2372 Assert(map->merged_indexes[index] == -1);
2373 Assert(!map->merged[index]);
2374 map->merged_indexes[index] = merged_index;
2375 /* Leave the merged flag alone! */
2376 *next_index = *next_index + 1;
2377 return merged_index;
2378}

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

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

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

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

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

Referenced by RelationBuildPartitionDesc().

◆ partition_bounds_create()

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

Definition at line 299 of file partbounds.c.

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

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

Referenced by RelationBuildPartitionDesc().

◆ partition_bounds_equal()

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

Definition at line 896 of file partbounds.c.

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

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

Referenced by compute_partition_bounds().

◆ partition_bounds_merge()

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

Definition at line 1118 of file partbounds.c.

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

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

Referenced by compute_partition_bounds().

◆ partition_hash_bsearch()

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

Definition at line 3738 of file partbounds.c.

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

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

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

3588{
3589 if (modulus1 < modulus2)
3590 return -1;
3591 if (modulus1 > modulus2)
3592 return 1;
3593 if (modulus1 == modulus2 && remainder1 != remainder2)
3594 return (remainder1 > remainder2) ? 1 : -1;
3595 return 0;
3596}

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

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

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

Referenced by check_new_partition_bound(), 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 3653 of file partbounds.c.

3657{
3658 int lo,
3659 hi,
3660 mid;
3661
3662 lo = -1;
3663 hi = boundinfo->ndatums - 1;
3664 while (lo < hi)
3665 {
3666 mid = (lo + hi + 1) / 2;
3667 *cmpval = partition_rbound_cmp(partnatts, partsupfunc,
3668 partcollation,
3669 boundinfo->datums[mid],
3670 boundinfo->kind[mid],
3671 (boundinfo->indexes[mid] == -1),
3672 probe);
3673 if (*cmpval <= 0)
3674 {
3675 lo = mid;
3676 if (*cmpval == 0)
3677 break;
3678 }
3679 else
3680 hi = mid - 1;
3681 }
3682
3683 return lo;
3684}

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,
Datum values,
bool *  is_equal 
)

Definition at line 3695 of file partbounds.c.

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

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

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

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(), and partition_range_bsearch().

◆ partition_rbound_datum_cmp()

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

Definition at line 3556 of file partbounds.c.

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

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

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

◆ partitions_are_ordered()

bool partitions_are_ordered ( PartitionBoundInfo  boundinfo,
Bitmapset live_parts 
)

Definition at line 2852 of file partbounds.c.

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

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

Referenced by build_partition_pathkeys(), and generate_orderedappend_paths().

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

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

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

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

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

3779{
3780 const PartitionHashBound *h1 = (const PartitionHashBound *) a;
3781 const PartitionHashBound *h2 = (const PartitionHashBound *) b;
3782
3783 return partition_hbound_cmp(h1->modulus, h1->remainder,
3784 h2->modulus, h2->remainder);
3785}
int b
Definition: isn.c:69
int a
Definition: isn.c:68

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

3794{
3795 Datum val1 = ((const PartitionListValue *) a)->value,
3796 val2 = ((const PartitionListValue *) b)->value;
3798
3799 return DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
3800 key->partcollation[0],
3801 val1, val2));
3802}
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 3810 of file partbounds.c.

3811{
3812 PartitionRangeBound *b1 = (*(PartitionRangeBound *const *) a);
3813 PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
3815
3816 return compare_range_bounds(key->partnatts, key->partsupfunc,
3817 key->partcollation,
3818 b1, b2);
3819}

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

Referenced by create_range_bounds().

◆ satisfies_hash_partition()

Datum satisfies_hash_partition ( PG_FUNCTION_ARGS  )

Definition at line 4771 of file partbounds.c.

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