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, const Datum *rb_datums, PartitionRangeDatumKind *rb_kind, const Datum *tuple_datums, int n_tuple_datums)
 
int partition_list_bsearch (FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
 
int partition_range_datum_bsearch (FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, int nvalues, const Datum *values, bool *is_equal)
 
int partition_hash_bsearch (PartitionBoundInfo boundinfo, int modulus, int remainder)
 
uint64 compute_partition_hash_value (int partnatts, FmgrInfo *partsupfunc, const Oid *partcollation, const Datum *values, const bool *isnull)
 
Datum satisfies_hash_partition (PG_FUNCTION_ARGS)
 

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

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

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

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

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

Referenced by merge_list_bounds(), and merge_range_bounds().

◆ check_default_partition_contents()

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

Definition at line 3243 of file partbounds.c.

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

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

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

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

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

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

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

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

Referenced by get_matching_hash_bounds(), and get_partition_for_tuple().

◆ create_hash_bounds()

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

Definition at line 347 of file partbounds.c.

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

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

Referenced by partition_bounds_create().

◆ create_list_bounds()

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

Definition at line 460 of file partbounds.c.

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

Referenced by partition_bounds_create().

◆ create_range_bounds()

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

Definition at line 674 of file partbounds.c.

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

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

Referenced by partition_bounds_create().

◆ fix_merged_indexes()

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

Definition at line 2378 of file partbounds.c.

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

Referenced by merge_list_bounds().

◆ free_partition_map()

static void free_partition_map ( PartitionMap map)
static

Definition at line 1825 of file partbounds.c.

1826{
1827 pfree(map->merged_indexes);
1828 pfree(map->merged);
1829 pfree(map->old_indexes);
1830}
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 2432 of file partbounds.c.

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

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

Referenced by merge_list_bounds(), and merge_range_bounds().

◆ get_hash_partition_greatest_modulus()

int get_hash_partition_greatest_modulus ( PartitionBoundInfo  bound)

Definition at line 3406 of file partbounds.c.

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

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

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

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

Referenced by merge_range_bounds().

◆ get_non_null_list_datum_count()

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

Definition at line 434 of file partbounds.c.

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

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

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

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

Referenced by make_partition_op_expr().

◆ get_qual_for_hash()

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

Definition at line 3974 of file partbounds.c.

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

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

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

References NullTest::arg, Assert(), PartitionDescData::boundinfo, BTEqualStrategyNumber, copyObject, datumCopy(), PartitionBoundInfoData::datums, i, PartitionBoundSpec::is_default, IS_NOT_NULL, IS_NULL, sort-test::key, lappend(), lfirst_node, linitial, list_make1, list_make2, PartitionBoundSpec::listdatums, NullTest::location, make_ands_explicit(), make_partition_op_expr(), makeBoolExpr(), makeConst(), makeNode, makeVar(), PartitionBoundInfoData::ndatums, NIL, NOT_EXPR, NullTest::nulltesttype, OR_EXPR, partition_bound_accepts_nulls, RelationGetPartitionDesc(), RelationGetPartitionKey(), and val.

Referenced by check_default_partition_contents(), and get_qual_from_partbound().

◆ get_qual_for_range()

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

Definition at line 4266 of file partbounds.c.

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

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

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

◆ get_qual_from_partbound()

List * get_qual_from_partbound ( Relation  parent,
PartitionBoundSpec spec 
)

Definition at line 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:3974

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

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

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

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

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

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

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

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

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

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

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

Referenced by merge_list_bounds(), and merge_range_bounds().

◆ is_dummy_partition()

static bool is_dummy_partition ( RelOptInfo rel,
int  part_index 
)
static

Definition at line 1836 of file partbounds.c.

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

References Assert(), and IS_DUMMY_REL.

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

◆ make_one_partition_rbound()

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

Definition at line 3420 of file partbounds.c.

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

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

Referenced by 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 3859 of file partbounds.c.

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

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

Referenced by get_qual_for_list(), and get_qual_for_range().

◆ merge_default_partitions()

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

Definition at line 2250 of file partbounds.c.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Referenced by RelationBuildPartitionDesc().

◆ partition_bounds_create()

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

Definition at line 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 = palloc_array(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:460
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:674

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

Referenced by RelationBuildPartitionDesc().

◆ partition_bounds_equal()

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

Definition at line 888 of file partbounds.c.

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

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

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

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

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

Referenced by check_new_partition_bound().

◆ partition_hbound_cmp()

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

Definition at line 3578 of file partbounds.c.

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

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

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

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

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

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

Referenced by check_new_partition_bound().

◆ partition_range_datum_bsearch()

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

Definition at line 3686 of file partbounds.c.

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

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

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

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,
const Datum rb_datums,
PartitionRangeDatumKind rb_kind,
const Datum tuple_datums,
int  n_tuple_datums 
)

Definition at line 3547 of file partbounds.c.

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

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

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

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

Referenced by build_partition_pathkeys(), and generate_orderedappend_paths().

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

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

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

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

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

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

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

Referenced by create_hash_bounds().

◆ qsort_partition_list_value_cmp()

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

Definition at line 3784 of file partbounds.c.

3785{
3786 Datum val1 = ((const PartitionListValue *) a)->value,
3787 val2 = ((const PartitionListValue *) b)->value;
3789
3790 return DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
3791 key->partcollation[0],
3792 val1, val2));
3793}
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 3801 of file partbounds.c.

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

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

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

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