Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * partbounds.c
4 : * Support routines for manipulating partition bounds
5 : *
6 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : * IDENTIFICATION
10 : * src/backend/partitioning/partbounds.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 :
15 : #include "postgres.h"
16 :
17 : #include "access/relation.h"
18 : #include "access/table.h"
19 : #include "access/tableam.h"
20 : #include "catalog/namespace.h"
21 : #include "catalog/partition.h"
22 : #include "catalog/pg_inherits.h"
23 : #include "catalog/pg_type.h"
24 : #include "commands/tablecmds.h"
25 : #include "common/hashfn.h"
26 : #include "executor/executor.h"
27 : #include "miscadmin.h"
28 : #include "nodes/makefuncs.h"
29 : #include "nodes/nodeFuncs.h"
30 : #include "nodes/pathnodes.h"
31 : #include "parser/parse_coerce.h"
32 : #include "partitioning/partbounds.h"
33 : #include "partitioning/partdesc.h"
34 : #include "utils/array.h"
35 : #include "utils/builtins.h"
36 : #include "utils/datum.h"
37 : #include "utils/fmgroids.h"
38 : #include "utils/lsyscache.h"
39 : #include "utils/partcache.h"
40 : #include "utils/ruleutils.h"
41 : #include "utils/snapmgr.h"
42 : #include "utils/syscache.h"
43 :
44 : /*
45 : * When qsort'ing partition bounds after reading from the catalog, each bound
46 : * is represented with one of the following structs.
47 : */
48 :
49 : /* One bound of a hash partition */
50 : typedef struct PartitionHashBound
51 : {
52 : int modulus;
53 : int remainder;
54 : int index;
55 : } PartitionHashBound;
56 :
57 : /* One value coming from some (index'th) list partition */
58 : typedef struct PartitionListValue
59 : {
60 : int index;
61 : Datum value;
62 : } PartitionListValue;
63 :
64 : /* One bound of a range partition */
65 : typedef struct PartitionRangeBound
66 : {
67 : int index;
68 : Datum *datums; /* range bound datums */
69 : PartitionRangeDatumKind *kind; /* the kind of each datum */
70 : bool lower; /* this is the lower (vs upper) bound */
71 : } PartitionRangeBound;
72 :
73 : /*
74 : * Mapping from partitions of a joining relation to partitions of a join
75 : * relation being computed (a.k.a merged partitions)
76 : */
77 : typedef struct PartitionMap
78 : {
79 : int nparts; /* number of partitions */
80 : int *merged_indexes; /* indexes of merged partitions */
81 : bool *merged; /* flags to indicate whether partitions are
82 : * merged with non-dummy partitions */
83 : bool did_remapping; /* did we re-map partitions? */
84 : int *old_indexes; /* old indexes of merged partitions if
85 : * did_remapping */
86 : } PartitionMap;
87 :
88 : /* Macro for comparing two range bounds */
89 : #define compare_range_bounds(partnatts, partsupfunc, partcollations, \
90 : bound1, bound2) \
91 : (partition_rbound_cmp(partnatts, partsupfunc, partcollations, \
92 : (bound1)->datums, (bound1)->kind, (bound1)->lower, \
93 : bound2))
94 :
95 : static int32 qsort_partition_hbound_cmp(const void *a, const void *b);
96 : static int32 qsort_partition_list_value_cmp(const void *a, const void *b,
97 : void *arg);
98 : static int32 qsort_partition_rbound_cmp(const void *a, const void *b,
99 : void *arg);
100 : static PartitionBoundInfo create_hash_bounds(PartitionBoundSpec **boundspecs,
101 : int nparts, PartitionKey key, int **mapping);
102 : static PartitionBoundInfo create_list_bounds(PartitionBoundSpec **boundspecs,
103 : int nparts, PartitionKey key, int **mapping);
104 : static PartitionBoundInfo create_range_bounds(PartitionBoundSpec **boundspecs,
105 : int nparts, PartitionKey key, int **mapping);
106 : static PartitionBoundInfo merge_list_bounds(FmgrInfo *partsupfunc,
107 : Oid *partcollation,
108 : RelOptInfo *outer_rel,
109 : RelOptInfo *inner_rel,
110 : JoinType jointype,
111 : List **outer_parts,
112 : List **inner_parts);
113 : static PartitionBoundInfo merge_range_bounds(int partnatts,
114 : FmgrInfo *partsupfuncs,
115 : Oid *partcollations,
116 : RelOptInfo *outer_rel,
117 : RelOptInfo *inner_rel,
118 : JoinType jointype,
119 : List **outer_parts,
120 : List **inner_parts);
121 : static void init_partition_map(RelOptInfo *rel, PartitionMap *map);
122 : static void free_partition_map(PartitionMap *map);
123 : static bool is_dummy_partition(RelOptInfo *rel, int part_index);
124 : static int merge_matching_partitions(PartitionMap *outer_map,
125 : PartitionMap *inner_map,
126 : int outer_index,
127 : int inner_index,
128 : int *next_index);
129 : static int process_outer_partition(PartitionMap *outer_map,
130 : PartitionMap *inner_map,
131 : bool outer_has_default,
132 : bool inner_has_default,
133 : int outer_index,
134 : int inner_default,
135 : JoinType jointype,
136 : int *next_index,
137 : int *default_index);
138 : static int process_inner_partition(PartitionMap *outer_map,
139 : PartitionMap *inner_map,
140 : bool outer_has_default,
141 : bool inner_has_default,
142 : int inner_index,
143 : int outer_default,
144 : JoinType jointype,
145 : int *next_index,
146 : int *default_index);
147 : static void merge_null_partitions(PartitionMap *outer_map,
148 : PartitionMap *inner_map,
149 : bool outer_has_null,
150 : bool inner_has_null,
151 : int outer_null,
152 : int inner_null,
153 : JoinType jointype,
154 : int *next_index,
155 : int *null_index);
156 : static void merge_default_partitions(PartitionMap *outer_map,
157 : PartitionMap *inner_map,
158 : bool outer_has_default,
159 : bool inner_has_default,
160 : int outer_default,
161 : int inner_default,
162 : JoinType jointype,
163 : int *next_index,
164 : int *default_index);
165 : static int merge_partition_with_dummy(PartitionMap *map, int index,
166 : int *next_index);
167 : static void fix_merged_indexes(PartitionMap *outer_map,
168 : PartitionMap *inner_map,
169 : int nmerged, List *merged_indexes);
170 : static void generate_matching_part_pairs(RelOptInfo *outer_rel,
171 : RelOptInfo *inner_rel,
172 : PartitionMap *outer_map,
173 : PartitionMap *inner_map,
174 : int nmerged,
175 : List **outer_parts,
176 : List **inner_parts);
177 : static PartitionBoundInfo build_merged_partition_bounds(char strategy,
178 : List *merged_datums,
179 : List *merged_kinds,
180 : List *merged_indexes,
181 : int null_index,
182 : int default_index);
183 : static int get_range_partition(RelOptInfo *rel,
184 : PartitionBoundInfo bi,
185 : int *lb_pos,
186 : PartitionRangeBound *lb,
187 : PartitionRangeBound *ub);
188 : static int get_range_partition_internal(PartitionBoundInfo bi,
189 : int *lb_pos,
190 : PartitionRangeBound *lb,
191 : PartitionRangeBound *ub);
192 : static bool compare_range_partitions(int partnatts, FmgrInfo *partsupfuncs,
193 : Oid *partcollations,
194 : PartitionRangeBound *outer_lb,
195 : PartitionRangeBound *outer_ub,
196 : PartitionRangeBound *inner_lb,
197 : PartitionRangeBound *inner_ub,
198 : int *lb_cmpval, int *ub_cmpval);
199 : static void get_merged_range_bounds(int partnatts, FmgrInfo *partsupfuncs,
200 : Oid *partcollations, JoinType jointype,
201 : PartitionRangeBound *outer_lb,
202 : PartitionRangeBound *outer_ub,
203 : PartitionRangeBound *inner_lb,
204 : PartitionRangeBound *inner_ub,
205 : int lb_cmpval, int ub_cmpval,
206 : PartitionRangeBound *merged_lb,
207 : PartitionRangeBound *merged_ub);
208 : static void add_merged_range_bounds(int partnatts, FmgrInfo *partsupfuncs,
209 : Oid *partcollations,
210 : PartitionRangeBound *merged_lb,
211 : PartitionRangeBound *merged_ub,
212 : int merged_index,
213 : List **merged_datums,
214 : List **merged_kinds,
215 : List **merged_indexes);
216 : static PartitionRangeBound *make_one_partition_rbound(PartitionKey key, int index,
217 : List *datums, bool lower);
218 : static int32 partition_hbound_cmp(int modulus1, int remainder1, int modulus2,
219 : int remainder2);
220 : static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
221 : Oid *partcollation, Datum *datums1,
222 : PartitionRangeDatumKind *kind1, bool lower1,
223 : PartitionRangeBound *b2);
224 : static int partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
225 : Oid *partcollation,
226 : PartitionBoundInfo boundinfo,
227 : PartitionRangeBound *probe, int32 *cmpval);
228 : static Expr *make_partition_op_expr(PartitionKey key, int keynum,
229 : uint16 strategy, Expr *arg1, Expr *arg2);
230 : static Oid get_partition_operator(PartitionKey key, int col,
231 : StrategyNumber strategy, bool *need_relabel);
232 : static List *get_qual_for_hash(Relation parent, PartitionBoundSpec *spec);
233 : static List *get_qual_for_list(Relation parent, PartitionBoundSpec *spec);
234 : static List *get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
235 : bool for_default);
236 : static void get_range_key_properties(PartitionKey key, int keynum,
237 : PartitionRangeDatum *ldatum,
238 : PartitionRangeDatum *udatum,
239 : ListCell **partexprs_item,
240 : Expr **keyCol,
241 : Const **lower_val, Const **upper_val);
242 : static List *get_range_nulltest(PartitionKey key);
243 :
244 : /*
245 : * get_qual_from_partbound
246 : * Given a parser node for partition bound, return the list of executable
247 : * expressions as partition constraint
248 : */
249 : List *
250 6524 : get_qual_from_partbound(Relation parent, PartitionBoundSpec *spec)
251 : {
252 6524 : PartitionKey key = RelationGetPartitionKey(parent);
253 6524 : List *my_qual = NIL;
254 :
255 : Assert(key != NULL);
256 :
257 6524 : switch (key->strategy)
258 : {
259 158 : case PARTITION_STRATEGY_HASH:
260 : Assert(spec->strategy == PARTITION_STRATEGY_HASH);
261 158 : my_qual = get_qual_for_hash(parent, spec);
262 158 : break;
263 :
264 2666 : case PARTITION_STRATEGY_LIST:
265 : Assert(spec->strategy == PARTITION_STRATEGY_LIST);
266 2666 : my_qual = get_qual_for_list(parent, spec);
267 2666 : break;
268 :
269 3700 : case PARTITION_STRATEGY_RANGE:
270 : Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
271 3700 : my_qual = get_qual_for_range(parent, spec, false);
272 3700 : break;
273 : }
274 :
275 6524 : return my_qual;
276 : }
277 :
278 : /*
279 : * partition_bounds_create
280 : * Build a PartitionBoundInfo struct from a list of PartitionBoundSpec
281 : * nodes
282 : *
283 : * This function creates a PartitionBoundInfo and fills the values of its
284 : * various members based on the input list. Importantly, 'datums' array will
285 : * contain Datum representation of individual bounds (possibly after
286 : * de-duplication as in case of range bounds), sorted in a canonical order
287 : * defined by qsort_partition_* functions of respective partitioning methods.
288 : * 'indexes' array will contain as many elements as there are bounds (specific
289 : * exceptions to this rule are listed in the function body), which represent
290 : * the 0-based canonical positions of partitions.
291 : *
292 : * Upon return from this function, *mapping is set to an array of
293 : * list_length(boundspecs) elements, each of which maps the original index of
294 : * a partition to its canonical index.
295 : *
296 : * Note: The objects returned by this function are wholly allocated in the
297 : * current memory context.
298 : */
299 : PartitionBoundInfo
300 18908 : partition_bounds_create(PartitionBoundSpec **boundspecs, int nparts,
301 : PartitionKey key, int **mapping)
302 : {
303 : int i;
304 :
305 : Assert(nparts > 0);
306 :
307 : /*
308 : * For each partitioning method, we first convert the partition bounds
309 : * from their parser node representation to the internal representation,
310 : * along with any additional preprocessing (such as de-duplicating range
311 : * bounds). Resulting bound datums are then added to the 'datums' array
312 : * in PartitionBoundInfo. For each datum added, an integer indicating the
313 : * canonical partition index is added to the 'indexes' array.
314 : *
315 : * For each bound, we remember its partition's position (0-based) in the
316 : * original list to later map it to the canonical index.
317 : */
318 :
319 : /*
320 : * Initialize mapping array with invalid values, this is filled within
321 : * each sub-routine below depending on the bound type.
322 : */
323 18908 : *mapping = palloc_array(int, nparts);
324 57566 : for (i = 0; i < nparts; i++)
325 38658 : (*mapping)[i] = -1;
326 :
327 18908 : switch (key->strategy)
328 : {
329 852 : case PARTITION_STRATEGY_HASH:
330 852 : return create_hash_bounds(boundspecs, nparts, key, mapping);
331 :
332 8534 : case PARTITION_STRATEGY_LIST:
333 8534 : return create_list_bounds(boundspecs, nparts, key, mapping);
334 :
335 9522 : case PARTITION_STRATEGY_RANGE:
336 9522 : return create_range_bounds(boundspecs, nparts, key, mapping);
337 : }
338 :
339 : Assert(false);
340 0 : return NULL; /* keep compiler quiet */
341 : }
342 :
343 : /*
344 : * create_hash_bounds
345 : * Create a PartitionBoundInfo for a hash partitioned table
346 : */
347 : static PartitionBoundInfo
348 852 : create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts,
349 : PartitionKey key, int **mapping)
350 : {
351 : PartitionBoundInfo boundinfo;
352 : PartitionHashBound *hbounds;
353 : int i;
354 : int greatest_modulus;
355 : Datum *boundDatums;
356 :
357 852 : boundinfo = palloc0_object(PartitionBoundInfoData);
358 852 : boundinfo->strategy = key->strategy;
359 : /* No special hash partitions. */
360 852 : boundinfo->null_index = -1;
361 852 : boundinfo->default_index = -1;
362 :
363 852 : hbounds = palloc_array(PartitionHashBound, nparts);
364 :
365 : /* Convert from node to the internal representation */
366 2662 : for (i = 0; i < nparts; i++)
367 : {
368 1810 : PartitionBoundSpec *spec = boundspecs[i];
369 :
370 1810 : if (spec->strategy != PARTITION_STRATEGY_HASH)
371 0 : elog(ERROR, "invalid strategy in partition bound spec");
372 :
373 1810 : hbounds[i].modulus = spec->modulus;
374 1810 : hbounds[i].remainder = spec->remainder;
375 1810 : hbounds[i].index = i;
376 : }
377 :
378 : /* Sort all the bounds in ascending order */
379 852 : qsort(hbounds, nparts, sizeof(PartitionHashBound),
380 : qsort_partition_hbound_cmp);
381 :
382 : /* After sorting, moduli are now stored in ascending order. */
383 852 : greatest_modulus = hbounds[nparts - 1].modulus;
384 :
385 852 : boundinfo->ndatums = nparts;
386 852 : boundinfo->datums = palloc0_array(Datum *, nparts);
387 852 : boundinfo->kind = NULL;
388 852 : boundinfo->interleaved_parts = NULL;
389 852 : boundinfo->nindexes = greatest_modulus;
390 852 : boundinfo->indexes = (int *) palloc(greatest_modulus * sizeof(int));
391 6512 : for (i = 0; i < greatest_modulus; i++)
392 5660 : boundinfo->indexes[i] = -1;
393 :
394 : /*
395 : * In the loop below, to save from allocating a series of small datum
396 : * arrays, here we just allocate a single array and below we'll just
397 : * assign a portion of this array per partition.
398 : */
399 852 : boundDatums = (Datum *) palloc(nparts * 2 * sizeof(Datum));
400 :
401 : /*
402 : * For hash partitioning, there are as many datums (modulus and remainder
403 : * pairs) as there are partitions. Indexes are simply values ranging from
404 : * 0 to (nparts - 1).
405 : */
406 2662 : for (i = 0; i < nparts; i++)
407 : {
408 1810 : int modulus = hbounds[i].modulus;
409 1810 : int remainder = hbounds[i].remainder;
410 :
411 1810 : boundinfo->datums[i] = &boundDatums[i * 2];
412 1810 : boundinfo->datums[i][0] = Int32GetDatum(modulus);
413 1810 : boundinfo->datums[i][1] = Int32GetDatum(remainder);
414 :
415 4094 : while (remainder < greatest_modulus)
416 : {
417 : /* overlap? */
418 : Assert(boundinfo->indexes[remainder] == -1);
419 2284 : boundinfo->indexes[remainder] = i;
420 2284 : remainder += modulus;
421 : }
422 :
423 1810 : (*mapping)[hbounds[i].index] = i;
424 : }
425 852 : pfree(hbounds);
426 :
427 852 : return boundinfo;
428 : }
429 :
430 : /*
431 : * get_non_null_list_datum_count
432 : * Counts the number of non-null Datums in each partition.
433 : */
434 : static int
435 8534 : get_non_null_list_datum_count(PartitionBoundSpec **boundspecs, int nparts)
436 : {
437 : int i;
438 8534 : int count = 0;
439 :
440 25158 : for (i = 0; i < nparts; i++)
441 : {
442 : ListCell *lc;
443 :
444 41952 : foreach(lc, boundspecs[i]->listdatums)
445 : {
446 25328 : Const *val = lfirst_node(Const, lc);
447 :
448 25328 : if (!val->constisnull)
449 24714 : count++;
450 : }
451 : }
452 :
453 8534 : return count;
454 : }
455 :
456 : /*
457 : * create_list_bounds
458 : * Create a PartitionBoundInfo for a list partitioned table
459 : */
460 : static PartitionBoundInfo
461 8534 : create_list_bounds(PartitionBoundSpec **boundspecs, int nparts,
462 : PartitionKey key, int **mapping)
463 : {
464 : PartitionBoundInfo boundinfo;
465 : PartitionListValue *all_values;
466 : int i;
467 : int j;
468 : int ndatums;
469 8534 : int next_index = 0;
470 8534 : int default_index = -1;
471 8534 : int null_index = -1;
472 : Datum *boundDatums;
473 :
474 8534 : boundinfo = palloc0_object(PartitionBoundInfoData);
475 8534 : boundinfo->strategy = key->strategy;
476 : /* Will be set correctly below. */
477 8534 : boundinfo->null_index = -1;
478 8534 : boundinfo->default_index = -1;
479 :
480 8534 : ndatums = get_non_null_list_datum_count(boundspecs, nparts);
481 : all_values = (PartitionListValue *)
482 8534 : palloc(ndatums * sizeof(PartitionListValue));
483 :
484 : /* Create a unified list of non-null values across all partitions. */
485 25158 : for (j = 0, i = 0; i < nparts; i++)
486 : {
487 16624 : PartitionBoundSpec *spec = boundspecs[i];
488 : ListCell *c;
489 :
490 16624 : if (spec->strategy != PARTITION_STRATEGY_LIST)
491 0 : elog(ERROR, "invalid strategy in partition bound spec");
492 :
493 : /*
494 : * Note the index of the partition bound spec for the default
495 : * partition. There's no datum to add to the list on non-null datums
496 : * for this partition.
497 : */
498 16624 : if (spec->is_default)
499 : {
500 966 : default_index = i;
501 966 : continue;
502 : }
503 :
504 40986 : foreach(c, spec->listdatums)
505 : {
506 25328 : Const *val = lfirst_node(Const, c);
507 :
508 25328 : if (!val->constisnull)
509 : {
510 24714 : all_values[j].index = i;
511 24714 : all_values[j].value = val->constvalue;
512 24714 : j++;
513 : }
514 : else
515 : {
516 : /*
517 : * Never put a null into the values array; save the index of
518 : * the partition that stores nulls, instead.
519 : */
520 614 : if (null_index != -1)
521 0 : elog(ERROR, "found null more than once");
522 614 : null_index = i;
523 : }
524 : }
525 : }
526 :
527 : /* ensure we found a Datum for every slot in the all_values array */
528 : Assert(j == ndatums);
529 :
530 8534 : qsort_arg(all_values, ndatums, sizeof(PartitionListValue),
531 : qsort_partition_list_value_cmp, key);
532 :
533 8534 : boundinfo->ndatums = ndatums;
534 8534 : boundinfo->datums = palloc0_array(Datum *, ndatums);
535 8534 : boundinfo->kind = NULL;
536 8534 : boundinfo->interleaved_parts = NULL;
537 8534 : boundinfo->nindexes = ndatums;
538 8534 : boundinfo->indexes = (int *) palloc(ndatums * sizeof(int));
539 :
540 : /*
541 : * In the loop below, to save from allocating a series of small datum
542 : * arrays, here we just allocate a single array and below we'll just
543 : * assign a portion of this array per datum.
544 : */
545 8534 : boundDatums = (Datum *) palloc(ndatums * sizeof(Datum));
546 :
547 : /*
548 : * Copy values. Canonical indexes are values ranging from 0 to (nparts -
549 : * 1) assigned to each partition such that all datums of a given partition
550 : * receive the same value. The value for a given partition is the index of
551 : * that partition's smallest datum in the all_values[] array.
552 : */
553 33248 : for (i = 0; i < ndatums; i++)
554 : {
555 24714 : int orig_index = all_values[i].index;
556 :
557 24714 : boundinfo->datums[i] = &boundDatums[i];
558 49428 : boundinfo->datums[i][0] = datumCopy(all_values[i].value,
559 24714 : key->parttypbyval[0],
560 24714 : key->parttyplen[0]);
561 :
562 : /* If the old index has no mapping, assign one */
563 24714 : if ((*mapping)[orig_index] == -1)
564 15454 : (*mapping)[orig_index] = next_index++;
565 :
566 24714 : boundinfo->indexes[i] = (*mapping)[orig_index];
567 : }
568 :
569 8534 : pfree(all_values);
570 :
571 : /*
572 : * Set the canonical value for null_index, if any.
573 : *
574 : * It is possible that the null-accepting partition has not been assigned
575 : * an index yet, which could happen if such partition accepts only null
576 : * and hence not handled in the above loop which only looked at non-null
577 : * values.
578 : */
579 8534 : if (null_index != -1)
580 : {
581 : Assert(null_index >= 0);
582 614 : if ((*mapping)[null_index] == -1)
583 204 : (*mapping)[null_index] = next_index++;
584 614 : boundinfo->null_index = (*mapping)[null_index];
585 : }
586 :
587 : /* Set the canonical value for default_index, if any. */
588 8534 : if (default_index != -1)
589 : {
590 : /*
591 : * The default partition accepts any value not specified in the lists
592 : * of other partitions, hence it should not get mapped index while
593 : * assigning those for non-null datums.
594 : */
595 : Assert(default_index >= 0);
596 : Assert((*mapping)[default_index] == -1);
597 966 : (*mapping)[default_index] = next_index++;
598 966 : boundinfo->default_index = (*mapping)[default_index];
599 : }
600 :
601 : /*
602 : * Calculate interleaved partitions. Here we look for partitions which
603 : * might be interleaved with other partitions and set a bit in
604 : * interleaved_parts for any partitions which may be interleaved with
605 : * another partition.
606 : */
607 :
608 : /*
609 : * There must be multiple partitions to have any interleaved partitions,
610 : * otherwise there's nothing to interleave with.
611 : */
612 8534 : if (nparts > 1)
613 : {
614 : /*
615 : * Short-circuit check to see if only 1 Datum is allowed per
616 : * partition. When this is true there's no need to do the more
617 : * expensive checks to look for interleaved values.
618 : */
619 5126 : if (boundinfo->ndatums +
620 5126 : partition_bound_accepts_nulls(boundinfo) +
621 5126 : partition_bound_has_default(boundinfo) != nparts)
622 : {
623 2070 : int last_index = -1;
624 :
625 : /*
626 : * Since the indexes array is sorted in Datum order, if any
627 : * partitions are interleaved then it will show up by the
628 : * partition indexes not being in ascending order. Here we check
629 : * for that and record all partitions that are out of order.
630 : */
631 15432 : for (i = 0; i < boundinfo->nindexes; i++)
632 : {
633 13362 : int index = boundinfo->indexes[i];
634 :
635 13362 : if (index < last_index)
636 1370 : boundinfo->interleaved_parts = bms_add_member(boundinfo->interleaved_parts,
637 : index);
638 :
639 : /*
640 : * Otherwise, if the null_index exists in the indexes array,
641 : * then the NULL partition must also allow some other Datum,
642 : * therefore it's "interleaved".
643 : */
644 11992 : else if (partition_bound_accepts_nulls(boundinfo) &&
645 3256 : index == boundinfo->null_index)
646 920 : boundinfo->interleaved_parts = bms_add_member(boundinfo->interleaved_parts,
647 : index);
648 :
649 13362 : last_index = index;
650 : }
651 : }
652 :
653 : /*
654 : * The DEFAULT partition is the "catch-all" partition that can contain
655 : * anything that does not belong to any other partition. If there are
656 : * any other partitions then the DEFAULT partition must be marked as
657 : * interleaved.
658 : */
659 5126 : if (partition_bound_has_default(boundinfo))
660 844 : boundinfo->interleaved_parts = bms_add_member(boundinfo->interleaved_parts,
661 : boundinfo->default_index);
662 : }
663 :
664 :
665 : /* All partitions must now have been assigned canonical indexes. */
666 : Assert(next_index == nparts);
667 8534 : return boundinfo;
668 : }
669 :
670 : /*
671 : * create_range_bounds
672 : * Create a PartitionBoundInfo for a range partitioned table
673 : */
674 : static PartitionBoundInfo
675 9522 : create_range_bounds(PartitionBoundSpec **boundspecs, int nparts,
676 : PartitionKey key, int **mapping)
677 : {
678 : PartitionBoundInfo boundinfo;
679 9522 : PartitionRangeBound **rbounds = NULL;
680 : PartitionRangeBound **all_bounds,
681 : *prev;
682 : int i,
683 : k,
684 : partnatts;
685 9522 : int ndatums = 0;
686 9522 : int default_index = -1;
687 9522 : int next_index = 0;
688 : Datum *boundDatums;
689 : PartitionRangeDatumKind *boundKinds;
690 :
691 9522 : boundinfo = palloc0_object(PartitionBoundInfoData);
692 9522 : boundinfo->strategy = key->strategy;
693 : /* There is no special null-accepting range partition. */
694 9522 : boundinfo->null_index = -1;
695 : /* Will be set correctly below. */
696 9522 : boundinfo->default_index = -1;
697 :
698 9522 : all_bounds = palloc0_array(PartitionRangeBound *, 2 * nparts);
699 :
700 : /* Create a unified list of range bounds across all the partitions. */
701 9522 : ndatums = 0;
702 29746 : for (i = 0; i < nparts; i++)
703 : {
704 20224 : PartitionBoundSpec *spec = boundspecs[i];
705 : PartitionRangeBound *lower,
706 : *upper;
707 :
708 20224 : if (spec->strategy != PARTITION_STRATEGY_RANGE)
709 0 : elog(ERROR, "invalid strategy in partition bound spec");
710 :
711 : /*
712 : * Note the index of the partition bound spec for the default
713 : * partition. There's no datum to add to the all_bounds array for
714 : * this partition.
715 : */
716 20224 : if (spec->is_default)
717 : {
718 1302 : default_index = i;
719 1302 : continue;
720 : }
721 :
722 18922 : lower = make_one_partition_rbound(key, i, spec->lowerdatums, true);
723 18922 : upper = make_one_partition_rbound(key, i, spec->upperdatums, false);
724 18922 : all_bounds[ndatums++] = lower;
725 18922 : all_bounds[ndatums++] = upper;
726 : }
727 :
728 : Assert(ndatums == nparts * 2 ||
729 : (default_index != -1 && ndatums == (nparts - 1) * 2));
730 :
731 : /* Sort all the bounds in ascending order */
732 9522 : qsort_arg(all_bounds, ndatums,
733 : sizeof(PartitionRangeBound *),
734 : qsort_partition_rbound_cmp,
735 : key);
736 :
737 : /* Save distinct bounds from all_bounds into rbounds. */
738 : rbounds = (PartitionRangeBound **)
739 9522 : palloc(ndatums * sizeof(PartitionRangeBound *));
740 9522 : k = 0;
741 9522 : prev = NULL;
742 47366 : for (i = 0; i < ndatums; i++)
743 : {
744 37844 : PartitionRangeBound *cur = all_bounds[i];
745 37844 : bool is_distinct = false;
746 : int j;
747 :
748 : /* Is the current bound distinct from the previous one? */
749 50370 : for (j = 0; j < key->partnatts; j++)
750 : {
751 : Datum cmpval;
752 :
753 42084 : if (prev == NULL || cur->kind[j] != prev->kind[j])
754 : {
755 10898 : is_distinct = true;
756 10898 : break;
757 : }
758 :
759 : /*
760 : * If the bounds are both MINVALUE or MAXVALUE, stop now and treat
761 : * them as equal, since any values after this point must be
762 : * ignored.
763 : */
764 31186 : if (cur->kind[j] != PARTITION_RANGE_DATUM_VALUE)
765 186 : break;
766 :
767 31000 : cmpval = FunctionCall2Coll(&key->partsupfunc[j],
768 31000 : key->partcollation[j],
769 31000 : cur->datums[j],
770 31000 : prev->datums[j]);
771 31000 : if (DatumGetInt32(cmpval) != 0)
772 : {
773 18474 : is_distinct = true;
774 18474 : break;
775 : }
776 : }
777 :
778 : /*
779 : * Only if the bound is distinct save it into a temporary array, i.e,
780 : * rbounds which is later copied into boundinfo datums array.
781 : */
782 37844 : if (is_distinct)
783 29372 : rbounds[k++] = all_bounds[i];
784 :
785 37844 : prev = cur;
786 : }
787 :
788 9522 : pfree(all_bounds);
789 :
790 : /* Update ndatums to hold the count of distinct datums. */
791 9522 : ndatums = k;
792 :
793 : /*
794 : * Add datums to boundinfo. Canonical indexes are values ranging from 0
795 : * to nparts - 1, assigned in that order to each partition's upper bound.
796 : * For 'datums' elements that are lower bounds, there is -1 in the
797 : * 'indexes' array to signify that no partition exists for the values less
798 : * than such a bound and greater than or equal to the previous upper
799 : * bound.
800 : */
801 9522 : boundinfo->ndatums = ndatums;
802 9522 : boundinfo->datums = palloc0_array(Datum *, ndatums);
803 9522 : boundinfo->kind = palloc0_array(PartitionRangeDatumKind *, ndatums);
804 9522 : boundinfo->interleaved_parts = NULL;
805 :
806 : /*
807 : * For range partitioning, an additional value of -1 is stored as the last
808 : * element of the indexes[] array.
809 : */
810 9522 : boundinfo->nindexes = ndatums + 1;
811 9522 : boundinfo->indexes = palloc_array(int, (ndatums + 1));
812 :
813 : /*
814 : * In the loop below, to save from allocating a series of small arrays,
815 : * here we just allocate a single array for Datums and another for
816 : * PartitionRangeDatumKinds, below we'll just assign a portion of these
817 : * arrays in each loop.
818 : */
819 9522 : partnatts = key->partnatts;
820 9522 : boundDatums = (Datum *) palloc(ndatums * partnatts * sizeof(Datum));
821 9522 : boundKinds = palloc_array(PartitionRangeDatumKind, ndatums * partnatts);
822 :
823 38894 : for (i = 0; i < ndatums; i++)
824 : {
825 : int j;
826 :
827 29372 : boundinfo->datums[i] = &boundDatums[i * partnatts];
828 29372 : boundinfo->kind[i] = &boundKinds[i * partnatts];
829 65232 : for (j = 0; j < partnatts; j++)
830 : {
831 35860 : if (rbounds[i]->kind[j] == PARTITION_RANGE_DATUM_VALUE)
832 33180 : boundinfo->datums[i][j] =
833 33180 : datumCopy(rbounds[i]->datums[j],
834 33180 : key->parttypbyval[j],
835 33180 : key->parttyplen[j]);
836 35860 : boundinfo->kind[i][j] = rbounds[i]->kind[j];
837 : }
838 :
839 : /*
840 : * There is no mapping for invalid indexes.
841 : *
842 : * Any lower bounds in the rbounds array have invalid indexes
843 : * assigned, because the values between the previous bound (if there
844 : * is one) and this (lower) bound are not part of the range of any
845 : * existing partition.
846 : */
847 29372 : if (rbounds[i]->lower)
848 10450 : boundinfo->indexes[i] = -1;
849 : else
850 : {
851 18922 : int orig_index = rbounds[i]->index;
852 :
853 : /* If the old index has no mapping, assign one */
854 18922 : if ((*mapping)[orig_index] == -1)
855 18922 : (*mapping)[orig_index] = next_index++;
856 :
857 18922 : boundinfo->indexes[i] = (*mapping)[orig_index];
858 : }
859 : }
860 :
861 9522 : pfree(rbounds);
862 :
863 : /* Set the canonical value for default_index, if any. */
864 9522 : if (default_index != -1)
865 : {
866 : Assert(default_index >= 0 && (*mapping)[default_index] == -1);
867 1302 : (*mapping)[default_index] = next_index++;
868 1302 : boundinfo->default_index = (*mapping)[default_index];
869 : }
870 :
871 : /* The extra -1 element. */
872 : Assert(i == ndatums);
873 9522 : boundinfo->indexes[i] = -1;
874 :
875 : /* All partitions must now have been assigned canonical indexes. */
876 : Assert(next_index == nparts);
877 9522 : return boundinfo;
878 : }
879 :
880 : /*
881 : * Are two partition bound collections logically equal?
882 : *
883 : * Used in the keep logic of relcache.c (ie, in RelationClearRelation()).
884 : * This is also useful when b1 and b2 are bound collections of two separate
885 : * relations, respectively, because PartitionBoundInfo is a canonical
886 : * representation of partition bounds.
887 : */
888 : bool
889 7068 : partition_bounds_equal(int partnatts, int16 *parttyplen, bool *parttypbyval,
890 : PartitionBoundInfo b1, PartitionBoundInfo b2)
891 : {
892 : int i;
893 :
894 7068 : if (b1->strategy != b2->strategy)
895 0 : return false;
896 :
897 7068 : if (b1->ndatums != b2->ndatums)
898 222 : return false;
899 :
900 6846 : if (b1->nindexes != b2->nindexes)
901 0 : return false;
902 :
903 6846 : if (b1->null_index != b2->null_index)
904 72 : return false;
905 :
906 6774 : if (b1->default_index != b2->default_index)
907 0 : return false;
908 :
909 : /* For all partition strategies, the indexes[] arrays have to match */
910 37868 : for (i = 0; i < b1->nindexes; i++)
911 : {
912 31142 : if (b1->indexes[i] != b2->indexes[i])
913 48 : return false;
914 : }
915 :
916 : /* Finally, compare the datums[] arrays */
917 6726 : if (b1->strategy == PARTITION_STRATEGY_HASH)
918 : {
919 : /*
920 : * We arrange the partitions in the ascending order of their moduli
921 : * and remainders. Also every modulus is factor of next larger
922 : * modulus. Therefore we can safely store index of a given partition
923 : * in indexes array at remainder of that partition. Also entries at
924 : * (remainder + N * modulus) positions in indexes array are all same
925 : * for (modulus, remainder) specification for any partition. Thus the
926 : * datums arrays from the given bounds are the same, if and only if
927 : * their indexes arrays are the same. So, it suffices to compare the
928 : * indexes arrays.
929 : *
930 : * Nonetheless make sure that the bounds are indeed the same when the
931 : * indexes match. Hash partition bound stores modulus and remainder
932 : * at b1->datums[i][0] and b1->datums[i][1] position respectively.
933 : */
934 : #ifdef USE_ASSERT_CHECKING
935 : for (i = 0; i < b1->ndatums; i++)
936 : Assert((b1->datums[i][0] == b2->datums[i][0] &&
937 : b1->datums[i][1] == b2->datums[i][1]));
938 : #endif
939 : }
940 : else
941 : {
942 30224 : for (i = 0; i < b1->ndatums; i++)
943 : {
944 : int j;
945 :
946 47374 : for (j = 0; j < partnatts; j++)
947 : {
948 : /* For range partitions, the bounds might not be finite. */
949 23804 : if (b1->kind != NULL)
950 : {
951 : /* The different kinds of bound all differ from each other */
952 22514 : if (b1->kind[i][j] != b2->kind[i][j])
953 0 : return false;
954 :
955 : /*
956 : * Non-finite bounds are equal without further
957 : * examination.
958 : */
959 22514 : if (b1->kind[i][j] != PARTITION_RANGE_DATUM_VALUE)
960 0 : continue;
961 : }
962 :
963 : /*
964 : * Compare the actual values. Note that it would be both
965 : * incorrect and unsafe to invoke the comparison operator
966 : * derived from the partitioning specification here. It would
967 : * be incorrect because we want the relcache entry to be
968 : * updated for ANY change to the partition bounds, not just
969 : * those that the partitioning operator thinks are
970 : * significant. It would be unsafe because we might reach
971 : * this code in the context of an aborted transaction, and an
972 : * arbitrary partitioning operator might not be safe in that
973 : * context. datumIsEqual() should be simple enough to be
974 : * safe.
975 : */
976 23804 : if (!datumIsEqual(b1->datums[i][j], b2->datums[i][j],
977 23804 : parttypbyval[j], parttyplen[j]))
978 186 : return false;
979 : }
980 : }
981 : }
982 6540 : return true;
983 : }
984 :
985 : /*
986 : * Return a copy of given PartitionBoundInfo structure. The data types of bounds
987 : * are described by given partition key specification.
988 : *
989 : * Note: it's important that this function and its callees not do any catalog
990 : * access, nor anything else that would result in allocating memory other than
991 : * the returned data structure. Since this is called in a long-lived context,
992 : * that would result in unwanted memory leaks.
993 : */
994 : PartitionBoundInfo
995 18908 : partition_bounds_copy(PartitionBoundInfo src,
996 : PartitionKey key)
997 : {
998 : PartitionBoundInfo dest;
999 : int i;
1000 : int ndatums;
1001 : int nindexes;
1002 : int partnatts;
1003 :
1004 18908 : dest = (PartitionBoundInfo) palloc_object(PartitionBoundInfoData);
1005 :
1006 18908 : dest->strategy = src->strategy;
1007 18908 : ndatums = dest->ndatums = src->ndatums;
1008 18908 : nindexes = dest->nindexes = src->nindexes;
1009 18908 : partnatts = key->partnatts;
1010 :
1011 : /* List partitioned tables have only a single partition key. */
1012 : Assert(key->strategy != PARTITION_STRATEGY_LIST || partnatts == 1);
1013 :
1014 18908 : dest->datums = palloc_array(Datum *, ndatums);
1015 :
1016 18908 : if (src->kind != NULL && ndatums > 0)
1017 9316 : {
1018 : PartitionRangeDatumKind *boundKinds;
1019 :
1020 : /* only RANGE partition should have a non-NULL kind */
1021 : Assert(key->strategy == PARTITION_STRATEGY_RANGE);
1022 :
1023 9316 : dest->kind = (PartitionRangeDatumKind **) palloc(ndatums *
1024 : sizeof(PartitionRangeDatumKind *));
1025 :
1026 : /*
1027 : * In the loop below, to save from allocating a series of small arrays
1028 : * for storing the PartitionRangeDatumKind, we allocate a single chunk
1029 : * here and use a smaller portion of it for each datum.
1030 : */
1031 9316 : boundKinds = (PartitionRangeDatumKind *) palloc(ndatums * partnatts *
1032 : sizeof(PartitionRangeDatumKind));
1033 :
1034 38688 : for (i = 0; i < ndatums; i++)
1035 : {
1036 29372 : dest->kind[i] = &boundKinds[i * partnatts];
1037 29372 : memcpy(dest->kind[i], src->kind[i],
1038 : sizeof(PartitionRangeDatumKind) * partnatts);
1039 : }
1040 : }
1041 : else
1042 9592 : dest->kind = NULL;
1043 :
1044 : /* copy interleaved partitions for LIST partitioned tables */
1045 18908 : dest->interleaved_parts = bms_copy(src->interleaved_parts);
1046 :
1047 : /*
1048 : * For hash partitioning, datums array will have two elements - modulus
1049 : * and remainder.
1050 : */
1051 18908 : if (ndatums > 0)
1052 : {
1053 18556 : bool hash_part = (key->strategy == PARTITION_STRATEGY_HASH);
1054 18556 : int natts = hash_part ? 2 : partnatts;
1055 18556 : Datum *boundDatums = palloc(ndatums * natts * sizeof(Datum));
1056 :
1057 74452 : for (i = 0; i < ndatums; i++)
1058 : {
1059 : int j;
1060 :
1061 55896 : dest->datums[i] = &boundDatums[i * natts];
1062 :
1063 120090 : for (j = 0; j < natts; j++)
1064 : {
1065 64194 : if (dest->kind == NULL ||
1066 35860 : dest->kind[i][j] == PARTITION_RANGE_DATUM_VALUE)
1067 : {
1068 : bool byval;
1069 : int typlen;
1070 :
1071 61514 : if (hash_part)
1072 : {
1073 3620 : typlen = sizeof(int32); /* Always int4 */
1074 3620 : byval = true; /* int4 is pass-by-value */
1075 : }
1076 : else
1077 : {
1078 57894 : byval = key->parttypbyval[j];
1079 57894 : typlen = key->parttyplen[j];
1080 : }
1081 61514 : dest->datums[i][j] = datumCopy(src->datums[i][j],
1082 : byval, typlen);
1083 : }
1084 : }
1085 : }
1086 : }
1087 :
1088 18908 : dest->indexes = palloc_array(int, nindexes);
1089 18908 : memcpy(dest->indexes, src->indexes, sizeof(int) * nindexes);
1090 :
1091 18908 : dest->null_index = src->null_index;
1092 18908 : dest->default_index = src->default_index;
1093 :
1094 18908 : return dest;
1095 : }
1096 :
1097 : /*
1098 : * partition_bounds_merge
1099 : * Check to see whether every partition of 'outer_rel' matches/overlaps
1100 : * one partition of 'inner_rel' at most, and vice versa; and if so, build
1101 : * and return the partition bounds for a join relation between the rels,
1102 : * generating two lists of the matching/overlapping partitions, which are
1103 : * returned to *outer_parts and *inner_parts respectively.
1104 : *
1105 : * The lists contain the same number of partitions, and the partitions at the
1106 : * same positions in the lists indicate join pairs used for partitioned join.
1107 : * If a partition on one side matches/overlaps multiple partitions on the other
1108 : * side, this function returns NULL, setting *outer_parts and *inner_parts to
1109 : * NIL.
1110 : */
1111 : PartitionBoundInfo
1112 846 : partition_bounds_merge(int partnatts,
1113 : FmgrInfo *partsupfunc, Oid *partcollation,
1114 : RelOptInfo *outer_rel, RelOptInfo *inner_rel,
1115 : JoinType jointype,
1116 : List **outer_parts, List **inner_parts)
1117 : {
1118 : /*
1119 : * Currently, this function is called only from try_partitionwise_join(),
1120 : * so the join type should be INNER, LEFT, FULL, SEMI, or ANTI.
1121 : */
1122 : Assert(jointype == JOIN_INNER || jointype == JOIN_LEFT ||
1123 : jointype == JOIN_FULL || jointype == JOIN_SEMI ||
1124 : jointype == JOIN_ANTI);
1125 :
1126 : /* The partitioning strategies should be the same. */
1127 : Assert(outer_rel->boundinfo->strategy == inner_rel->boundinfo->strategy);
1128 :
1129 846 : *outer_parts = *inner_parts = NIL;
1130 846 : switch (outer_rel->boundinfo->strategy)
1131 : {
1132 0 : case PARTITION_STRATEGY_HASH:
1133 :
1134 : /*
1135 : * For hash partitioned tables, we currently support partitioned
1136 : * join only when they have exactly the same partition bounds.
1137 : *
1138 : * XXX: it might be possible to relax the restriction to support
1139 : * cases where hash partitioned tables have missing partitions
1140 : * and/or different moduli, but it's not clear if it would be
1141 : * useful to support the former case since it's unusual to have
1142 : * missing partitions. On the other hand, it would be useful to
1143 : * support the latter case, but in that case, there is a high
1144 : * probability that a partition on one side will match multiple
1145 : * partitions on the other side, which is the scenario the current
1146 : * implementation of partitioned join can't handle.
1147 : */
1148 0 : return NULL;
1149 :
1150 486 : case PARTITION_STRATEGY_LIST:
1151 486 : return merge_list_bounds(partsupfunc,
1152 : partcollation,
1153 : outer_rel,
1154 : inner_rel,
1155 : jointype,
1156 : outer_parts,
1157 : inner_parts);
1158 :
1159 360 : case PARTITION_STRATEGY_RANGE:
1160 360 : return merge_range_bounds(partnatts,
1161 : partsupfunc,
1162 : partcollation,
1163 : outer_rel,
1164 : inner_rel,
1165 : jointype,
1166 : outer_parts,
1167 : inner_parts);
1168 : }
1169 :
1170 0 : return NULL;
1171 : }
1172 :
1173 : /*
1174 : * merge_list_bounds
1175 : * Create the partition bounds for a join relation between list
1176 : * partitioned tables, if possible
1177 : *
1178 : * In this function we try to find sets of matching partitions from both sides
1179 : * by comparing list values stored in their partition bounds. Since the list
1180 : * values appear in the ascending order, an algorithm similar to merge join is
1181 : * used for that. If a partition on one side doesn't have a matching
1182 : * partition on the other side, the algorithm tries to match it with the
1183 : * default partition on the other side if any; if not, the algorithm tries to
1184 : * match it with a dummy partition on the other side if it's on the
1185 : * non-nullable side of an outer join. Also, if both sides have the default
1186 : * partitions, the algorithm tries to match them with each other. We give up
1187 : * if the algorithm finds a partition matching multiple partitions on the
1188 : * other side, which is the scenario the current implementation of partitioned
1189 : * join can't handle.
1190 : */
1191 : static PartitionBoundInfo
1192 486 : merge_list_bounds(FmgrInfo *partsupfunc, Oid *partcollation,
1193 : RelOptInfo *outer_rel, RelOptInfo *inner_rel,
1194 : JoinType jointype,
1195 : List **outer_parts, List **inner_parts)
1196 : {
1197 486 : PartitionBoundInfo merged_bounds = NULL;
1198 486 : PartitionBoundInfo outer_bi = outer_rel->boundinfo;
1199 486 : PartitionBoundInfo inner_bi = inner_rel->boundinfo;
1200 486 : bool outer_has_default = partition_bound_has_default(outer_bi);
1201 486 : bool inner_has_default = partition_bound_has_default(inner_bi);
1202 486 : int outer_default = outer_bi->default_index;
1203 486 : int inner_default = inner_bi->default_index;
1204 486 : bool outer_has_null = partition_bound_accepts_nulls(outer_bi);
1205 486 : bool inner_has_null = partition_bound_accepts_nulls(inner_bi);
1206 : PartitionMap outer_map;
1207 : PartitionMap inner_map;
1208 : int outer_pos;
1209 : int inner_pos;
1210 486 : int next_index = 0;
1211 486 : int null_index = -1;
1212 486 : int default_index = -1;
1213 486 : List *merged_datums = NIL;
1214 486 : List *merged_indexes = NIL;
1215 :
1216 : Assert(*outer_parts == NIL);
1217 : Assert(*inner_parts == NIL);
1218 : Assert(outer_bi->strategy == inner_bi->strategy &&
1219 : outer_bi->strategy == PARTITION_STRATEGY_LIST);
1220 : /* List partitioning doesn't require kinds. */
1221 : Assert(!outer_bi->kind && !inner_bi->kind);
1222 :
1223 486 : init_partition_map(outer_rel, &outer_map);
1224 486 : init_partition_map(inner_rel, &inner_map);
1225 :
1226 : /*
1227 : * If the default partitions (if any) have been proven empty, deem them
1228 : * non-existent.
1229 : */
1230 486 : if (outer_has_default && is_dummy_partition(outer_rel, outer_default))
1231 24 : outer_has_default = false;
1232 486 : if (inner_has_default && is_dummy_partition(inner_rel, inner_default))
1233 0 : inner_has_default = false;
1234 :
1235 : /*
1236 : * Merge partitions from both sides. In each iteration we compare a pair
1237 : * of list values, one from each side, and decide whether the
1238 : * corresponding partitions match or not. If the two values match
1239 : * exactly, move to the next pair of list values, otherwise move to the
1240 : * next list value on the side with a smaller list value.
1241 : */
1242 486 : outer_pos = inner_pos = 0;
1243 3822 : while (outer_pos < outer_bi->ndatums || inner_pos < inner_bi->ndatums)
1244 : {
1245 3384 : int outer_index = -1;
1246 3384 : int inner_index = -1;
1247 : Datum *outer_datums;
1248 : Datum *inner_datums;
1249 : int cmpval;
1250 3384 : Datum *merged_datum = NULL;
1251 3384 : int merged_index = -1;
1252 :
1253 3384 : if (outer_pos < outer_bi->ndatums)
1254 : {
1255 : /*
1256 : * If the partition on the outer side has been proven empty,
1257 : * ignore it and move to the next datum on the outer side.
1258 : */
1259 3336 : outer_index = outer_bi->indexes[outer_pos];
1260 3336 : if (is_dummy_partition(outer_rel, outer_index))
1261 : {
1262 168 : outer_pos++;
1263 168 : continue;
1264 : }
1265 : }
1266 3216 : if (inner_pos < inner_bi->ndatums)
1267 : {
1268 : /*
1269 : * If the partition on the inner side has been proven empty,
1270 : * ignore it and move to the next datum on the inner side.
1271 : */
1272 3216 : inner_index = inner_bi->indexes[inner_pos];
1273 3216 : if (is_dummy_partition(inner_rel, inner_index))
1274 : {
1275 0 : inner_pos++;
1276 0 : continue;
1277 : }
1278 : }
1279 :
1280 : /* Get the list values. */
1281 6432 : outer_datums = outer_pos < outer_bi->ndatums ?
1282 3216 : outer_bi->datums[outer_pos] : NULL;
1283 6432 : inner_datums = inner_pos < inner_bi->ndatums ?
1284 3216 : inner_bi->datums[inner_pos] : NULL;
1285 :
1286 : /*
1287 : * We run this loop till both sides finish. This allows us to avoid
1288 : * duplicating code to handle the remaining values on the side which
1289 : * finishes later. For that we set the comparison parameter cmpval in
1290 : * such a way that it appears as if the side which finishes earlier
1291 : * has an extra value higher than any other value on the unfinished
1292 : * side. That way we advance the values on the unfinished side till
1293 : * all of its values are exhausted.
1294 : */
1295 3216 : if (outer_pos >= outer_bi->ndatums)
1296 48 : cmpval = 1;
1297 3168 : else if (inner_pos >= inner_bi->ndatums)
1298 0 : cmpval = -1;
1299 : else
1300 : {
1301 : Assert(outer_datums != NULL && inner_datums != NULL);
1302 3168 : cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
1303 : partcollation[0],
1304 : outer_datums[0],
1305 : inner_datums[0]));
1306 : }
1307 :
1308 3216 : if (cmpval == 0)
1309 : {
1310 : /* Two list values match exactly. */
1311 : Assert(outer_pos < outer_bi->ndatums);
1312 : Assert(inner_pos < inner_bi->ndatums);
1313 : Assert(outer_index >= 0);
1314 : Assert(inner_index >= 0);
1315 :
1316 : /*
1317 : * Try merging both partitions. If successful, add the list value
1318 : * and index of the merged partition below.
1319 : */
1320 1644 : merged_index = merge_matching_partitions(&outer_map, &inner_map,
1321 : outer_index, inner_index,
1322 : &next_index);
1323 1644 : if (merged_index == -1)
1324 30 : goto cleanup;
1325 :
1326 1614 : merged_datum = outer_datums;
1327 :
1328 : /* Move to the next pair of list values. */
1329 1614 : outer_pos++;
1330 1614 : inner_pos++;
1331 : }
1332 1572 : else if (cmpval < 0)
1333 : {
1334 : /* A list value missing from the inner side. */
1335 : Assert(outer_pos < outer_bi->ndatums);
1336 :
1337 : /*
1338 : * If the inner side has the default partition, or this is an
1339 : * outer join, try to assign a merged partition to the outer
1340 : * partition (see process_outer_partition()). Otherwise, the
1341 : * outer partition will not contribute to the result.
1342 : */
1343 636 : if (inner_has_default || IS_OUTER_JOIN(jointype))
1344 : {
1345 : /* Get the outer partition. */
1346 408 : outer_index = outer_bi->indexes[outer_pos];
1347 : Assert(outer_index >= 0);
1348 408 : merged_index = process_outer_partition(&outer_map,
1349 : &inner_map,
1350 : outer_has_default,
1351 : inner_has_default,
1352 : outer_index,
1353 : inner_default,
1354 : jointype,
1355 : &next_index,
1356 : &default_index);
1357 408 : if (merged_index == -1)
1358 6 : goto cleanup;
1359 402 : merged_datum = outer_datums;
1360 : }
1361 :
1362 : /* Move to the next list value on the outer side. */
1363 630 : outer_pos++;
1364 : }
1365 : else
1366 : {
1367 : /* A list value missing from the outer side. */
1368 : Assert(cmpval > 0);
1369 : Assert(inner_pos < inner_bi->ndatums);
1370 :
1371 : /*
1372 : * If the outer side has the default partition, or this is a FULL
1373 : * join, try to assign a merged partition to the inner partition
1374 : * (see process_inner_partition()). Otherwise, the inner
1375 : * partition will not contribute to the result.
1376 : */
1377 936 : if (outer_has_default || jointype == JOIN_FULL)
1378 : {
1379 : /* Get the inner partition. */
1380 252 : inner_index = inner_bi->indexes[inner_pos];
1381 : Assert(inner_index >= 0);
1382 252 : merged_index = process_inner_partition(&outer_map,
1383 : &inner_map,
1384 : outer_has_default,
1385 : inner_has_default,
1386 : inner_index,
1387 : outer_default,
1388 : jointype,
1389 : &next_index,
1390 : &default_index);
1391 252 : if (merged_index == -1)
1392 12 : goto cleanup;
1393 240 : merged_datum = inner_datums;
1394 : }
1395 :
1396 : /* Move to the next list value on the inner side. */
1397 924 : inner_pos++;
1398 : }
1399 :
1400 : /*
1401 : * If we assigned a merged partition, add the list value and index of
1402 : * the merged partition if appropriate.
1403 : */
1404 3168 : if (merged_index >= 0 && merged_index != default_index)
1405 : {
1406 2184 : merged_datums = lappend(merged_datums, merged_datum);
1407 2184 : merged_indexes = lappend_int(merged_indexes, merged_index);
1408 : }
1409 : }
1410 :
1411 : /*
1412 : * If the NULL partitions (if any) have been proven empty, deem them
1413 : * non-existent.
1414 : */
1415 630 : if (outer_has_null &&
1416 192 : is_dummy_partition(outer_rel, outer_bi->null_index))
1417 0 : outer_has_null = false;
1418 630 : if (inner_has_null &&
1419 192 : is_dummy_partition(inner_rel, inner_bi->null_index))
1420 0 : inner_has_null = false;
1421 :
1422 : /* Merge the NULL partitions if any. */
1423 438 : if (outer_has_null || inner_has_null)
1424 216 : merge_null_partitions(&outer_map, &inner_map,
1425 : outer_has_null, inner_has_null,
1426 : outer_bi->null_index, inner_bi->null_index,
1427 : jointype, &next_index, &null_index);
1428 : else
1429 : Assert(null_index == -1);
1430 :
1431 : /* Merge the default partitions if any. */
1432 438 : if (outer_has_default || inner_has_default)
1433 96 : merge_default_partitions(&outer_map, &inner_map,
1434 : outer_has_default, inner_has_default,
1435 : outer_default, inner_default,
1436 : jointype, &next_index, &default_index);
1437 : else
1438 : Assert(default_index == -1);
1439 :
1440 : /* If we have merged partitions, create the partition bounds. */
1441 438 : if (next_index > 0)
1442 : {
1443 : /* Fix the merged_indexes list if necessary. */
1444 438 : if (outer_map.did_remapping || inner_map.did_remapping)
1445 : {
1446 : Assert(jointype == JOIN_FULL);
1447 48 : fix_merged_indexes(&outer_map, &inner_map,
1448 : next_index, merged_indexes);
1449 : }
1450 :
1451 : /* Use maps to match partitions from inputs. */
1452 438 : generate_matching_part_pairs(outer_rel, inner_rel,
1453 : &outer_map, &inner_map,
1454 : next_index,
1455 : outer_parts, inner_parts);
1456 : Assert(*outer_parts != NIL);
1457 : Assert(*inner_parts != NIL);
1458 : Assert(list_length(*outer_parts) == list_length(*inner_parts));
1459 : Assert(list_length(*outer_parts) <= next_index);
1460 :
1461 : /* Make a PartitionBoundInfo struct to return. */
1462 438 : merged_bounds = build_merged_partition_bounds(outer_bi->strategy,
1463 : merged_datums,
1464 : NIL,
1465 : merged_indexes,
1466 : null_index,
1467 : default_index);
1468 : Assert(merged_bounds);
1469 : }
1470 :
1471 0 : cleanup:
1472 : /* Free local memory before returning. */
1473 486 : list_free(merged_datums);
1474 486 : list_free(merged_indexes);
1475 486 : free_partition_map(&outer_map);
1476 486 : free_partition_map(&inner_map);
1477 :
1478 486 : return merged_bounds;
1479 : }
1480 :
1481 : /*
1482 : * merge_range_bounds
1483 : * Create the partition bounds for a join relation between range
1484 : * partitioned tables, if possible
1485 : *
1486 : * In this function we try to find sets of overlapping partitions from both
1487 : * sides by comparing ranges stored in their partition bounds. Since the
1488 : * ranges appear in the ascending order, an algorithm similar to merge join is
1489 : * used for that. If a partition on one side doesn't have an overlapping
1490 : * partition on the other side, the algorithm tries to match it with the
1491 : * default partition on the other side if any; if not, the algorithm tries to
1492 : * match it with a dummy partition on the other side if it's on the
1493 : * non-nullable side of an outer join. Also, if both sides have the default
1494 : * partitions, the algorithm tries to match them with each other. We give up
1495 : * if the algorithm finds a partition overlapping multiple partitions on the
1496 : * other side, which is the scenario the current implementation of partitioned
1497 : * join can't handle.
1498 : */
1499 : static PartitionBoundInfo
1500 360 : merge_range_bounds(int partnatts, FmgrInfo *partsupfuncs,
1501 : Oid *partcollations,
1502 : RelOptInfo *outer_rel, RelOptInfo *inner_rel,
1503 : JoinType jointype,
1504 : List **outer_parts, List **inner_parts)
1505 : {
1506 360 : PartitionBoundInfo merged_bounds = NULL;
1507 360 : PartitionBoundInfo outer_bi = outer_rel->boundinfo;
1508 360 : PartitionBoundInfo inner_bi = inner_rel->boundinfo;
1509 360 : bool outer_has_default = partition_bound_has_default(outer_bi);
1510 360 : bool inner_has_default = partition_bound_has_default(inner_bi);
1511 360 : int outer_default = outer_bi->default_index;
1512 360 : int inner_default = inner_bi->default_index;
1513 : PartitionMap outer_map;
1514 : PartitionMap inner_map;
1515 : int outer_index;
1516 : int inner_index;
1517 : int outer_lb_pos;
1518 : int inner_lb_pos;
1519 : PartitionRangeBound outer_lb;
1520 : PartitionRangeBound outer_ub;
1521 : PartitionRangeBound inner_lb;
1522 : PartitionRangeBound inner_ub;
1523 360 : int next_index = 0;
1524 360 : int default_index = -1;
1525 360 : List *merged_datums = NIL;
1526 360 : List *merged_kinds = NIL;
1527 360 : List *merged_indexes = NIL;
1528 :
1529 : Assert(*outer_parts == NIL);
1530 : Assert(*inner_parts == NIL);
1531 : Assert(outer_bi->strategy == inner_bi->strategy &&
1532 : outer_bi->strategy == PARTITION_STRATEGY_RANGE);
1533 :
1534 360 : init_partition_map(outer_rel, &outer_map);
1535 360 : init_partition_map(inner_rel, &inner_map);
1536 :
1537 : /*
1538 : * If the default partitions (if any) have been proven empty, deem them
1539 : * non-existent.
1540 : */
1541 360 : if (outer_has_default && is_dummy_partition(outer_rel, outer_default))
1542 12 : outer_has_default = false;
1543 360 : if (inner_has_default && is_dummy_partition(inner_rel, inner_default))
1544 0 : inner_has_default = false;
1545 :
1546 : /*
1547 : * Merge partitions from both sides. In each iteration we compare a pair
1548 : * of ranges, one from each side, and decide whether the corresponding
1549 : * partitions match or not. If the two ranges overlap, move to the next
1550 : * pair of ranges, otherwise move to the next range on the side with a
1551 : * lower range. outer_lb_pos/inner_lb_pos keep track of the positions of
1552 : * lower bounds in the datums arrays in the outer/inner
1553 : * PartitionBoundInfos respectively.
1554 : */
1555 360 : outer_lb_pos = inner_lb_pos = 0;
1556 360 : outer_index = get_range_partition(outer_rel, outer_bi, &outer_lb_pos,
1557 : &outer_lb, &outer_ub);
1558 360 : inner_index = get_range_partition(inner_rel, inner_bi, &inner_lb_pos,
1559 : &inner_lb, &inner_ub);
1560 1284 : while (outer_index >= 0 || inner_index >= 0)
1561 : {
1562 : bool overlap;
1563 : int ub_cmpval;
1564 : int lb_cmpval;
1565 990 : PartitionRangeBound merged_lb = {-1, NULL, NULL, true};
1566 990 : PartitionRangeBound merged_ub = {-1, NULL, NULL, false};
1567 990 : int merged_index = -1;
1568 :
1569 : /*
1570 : * We run this loop till both sides finish. This allows us to avoid
1571 : * duplicating code to handle the remaining ranges on the side which
1572 : * finishes later. For that we set the comparison parameter cmpval in
1573 : * such a way that it appears as if the side which finishes earlier
1574 : * has an extra range higher than any other range on the unfinished
1575 : * side. That way we advance the ranges on the unfinished side till
1576 : * all of its ranges are exhausted.
1577 : */
1578 990 : if (outer_index == -1)
1579 : {
1580 90 : overlap = false;
1581 90 : lb_cmpval = 1;
1582 90 : ub_cmpval = 1;
1583 : }
1584 900 : else if (inner_index == -1)
1585 : {
1586 36 : overlap = false;
1587 36 : lb_cmpval = -1;
1588 36 : ub_cmpval = -1;
1589 : }
1590 : else
1591 864 : overlap = compare_range_partitions(partnatts, partsupfuncs,
1592 : partcollations,
1593 : &outer_lb, &outer_ub,
1594 : &inner_lb, &inner_ub,
1595 : &lb_cmpval, &ub_cmpval);
1596 :
1597 990 : if (overlap)
1598 : {
1599 : /* Two ranges overlap; form a join pair. */
1600 :
1601 : PartitionRangeBound save_outer_ub;
1602 : PartitionRangeBound save_inner_ub;
1603 :
1604 : /* Both partitions should not have been merged yet. */
1605 : Assert(outer_index >= 0);
1606 : Assert(outer_map.merged_indexes[outer_index] == -1 &&
1607 : outer_map.merged[outer_index] == false);
1608 : Assert(inner_index >= 0);
1609 : Assert(inner_map.merged_indexes[inner_index] == -1 &&
1610 : inner_map.merged[inner_index] == false);
1611 :
1612 : /*
1613 : * Get the index of the merged partition. Both partitions aren't
1614 : * merged yet, so the partitions should be merged successfully.
1615 : */
1616 828 : merged_index = merge_matching_partitions(&outer_map, &inner_map,
1617 : outer_index, inner_index,
1618 : &next_index);
1619 : Assert(merged_index >= 0);
1620 :
1621 : /* Get the range bounds of the merged partition. */
1622 828 : get_merged_range_bounds(partnatts, partsupfuncs,
1623 : partcollations, jointype,
1624 : &outer_lb, &outer_ub,
1625 : &inner_lb, &inner_ub,
1626 : lb_cmpval, ub_cmpval,
1627 : &merged_lb, &merged_ub);
1628 :
1629 : /* Save the upper bounds of both partitions for use below. */
1630 828 : save_outer_ub = outer_ub;
1631 828 : save_inner_ub = inner_ub;
1632 :
1633 : /* Move to the next pair of ranges. */
1634 828 : outer_index = get_range_partition(outer_rel, outer_bi, &outer_lb_pos,
1635 : &outer_lb, &outer_ub);
1636 828 : inner_index = get_range_partition(inner_rel, inner_bi, &inner_lb_pos,
1637 : &inner_lb, &inner_ub);
1638 :
1639 : /*
1640 : * If the range of a partition on one side overlaps the range of
1641 : * the next partition on the other side, that will cause the
1642 : * partition on one side to match at least two partitions on the
1643 : * other side, which is the case that we currently don't support
1644 : * partitioned join for; give up.
1645 : */
1646 1032 : if (ub_cmpval > 0 && inner_index >= 0 &&
1647 204 : compare_range_bounds(partnatts, partsupfuncs, partcollations,
1648 : &save_outer_ub, &inner_lb) > 0)
1649 60 : goto cleanup;
1650 858 : if (ub_cmpval < 0 && outer_index >= 0 &&
1651 66 : compare_range_bounds(partnatts, partsupfuncs, partcollations,
1652 : &outer_lb, &save_inner_ub) < 0)
1653 18 : goto cleanup;
1654 :
1655 : /*
1656 : * A row from a non-overlapping portion (if any) of a partition on
1657 : * one side might find its join partner in the default partition
1658 : * (if any) on the other side, causing the same situation as
1659 : * above; give up in that case.
1660 : */
1661 774 : if ((outer_has_default && (lb_cmpval > 0 || ub_cmpval < 0)) ||
1662 24 : (inner_has_default && (lb_cmpval < 0 || ub_cmpval > 0)))
1663 6 : goto cleanup;
1664 : }
1665 162 : else if (ub_cmpval < 0)
1666 : {
1667 : /* A non-overlapping outer range. */
1668 :
1669 : /* The outer partition should not have been merged yet. */
1670 : Assert(outer_index >= 0);
1671 : Assert(outer_map.merged_indexes[outer_index] == -1 &&
1672 : outer_map.merged[outer_index] == false);
1673 :
1674 : /*
1675 : * If the inner side has the default partition, or this is an
1676 : * outer join, try to assign a merged partition to the outer
1677 : * partition (see process_outer_partition()). Otherwise, the
1678 : * outer partition will not contribute to the result.
1679 : */
1680 36 : if (inner_has_default || IS_OUTER_JOIN(jointype))
1681 : {
1682 24 : merged_index = process_outer_partition(&outer_map,
1683 : &inner_map,
1684 : outer_has_default,
1685 : inner_has_default,
1686 : outer_index,
1687 : inner_default,
1688 : jointype,
1689 : &next_index,
1690 : &default_index);
1691 24 : if (merged_index == -1)
1692 0 : goto cleanup;
1693 24 : merged_lb = outer_lb;
1694 24 : merged_ub = outer_ub;
1695 : }
1696 :
1697 : /* Move to the next range on the outer side. */
1698 36 : outer_index = get_range_partition(outer_rel, outer_bi, &outer_lb_pos,
1699 : &outer_lb, &outer_ub);
1700 : }
1701 : else
1702 : {
1703 : /* A non-overlapping inner range. */
1704 : Assert(ub_cmpval > 0);
1705 :
1706 : /* The inner partition should not have been merged yet. */
1707 : Assert(inner_index >= 0);
1708 : Assert(inner_map.merged_indexes[inner_index] == -1 &&
1709 : inner_map.merged[inner_index] == false);
1710 :
1711 : /*
1712 : * If the outer side has the default partition, or this is a FULL
1713 : * join, try to assign a merged partition to the inner partition
1714 : * (see process_inner_partition()). Otherwise, the inner
1715 : * partition will not contribute to the result.
1716 : */
1717 126 : if (outer_has_default || jointype == JOIN_FULL)
1718 : {
1719 66 : merged_index = process_inner_partition(&outer_map,
1720 : &inner_map,
1721 : outer_has_default,
1722 : inner_has_default,
1723 : inner_index,
1724 : outer_default,
1725 : jointype,
1726 : &next_index,
1727 : &default_index);
1728 66 : if (merged_index == -1)
1729 6 : goto cleanup;
1730 60 : merged_lb = inner_lb;
1731 60 : merged_ub = inner_ub;
1732 : }
1733 :
1734 : /* Move to the next range on the inner side. */
1735 120 : inner_index = get_range_partition(inner_rel, inner_bi, &inner_lb_pos,
1736 : &inner_lb, &inner_ub);
1737 : }
1738 :
1739 : /*
1740 : * If we assigned a merged partition, add the range bounds and index
1741 : * of the merged partition if appropriate.
1742 : */
1743 924 : if (merged_index >= 0 && merged_index != default_index)
1744 816 : add_merged_range_bounds(partnatts, partsupfuncs, partcollations,
1745 : &merged_lb, &merged_ub, merged_index,
1746 : &merged_datums, &merged_kinds,
1747 : &merged_indexes);
1748 : }
1749 :
1750 : /* Merge the default partitions if any. */
1751 294 : if (outer_has_default || inner_has_default)
1752 60 : merge_default_partitions(&outer_map, &inner_map,
1753 : outer_has_default, inner_has_default,
1754 : outer_default, inner_default,
1755 : jointype, &next_index, &default_index);
1756 : else
1757 : Assert(default_index == -1);
1758 :
1759 : /* If we have merged partitions, create the partition bounds. */
1760 294 : if (next_index > 0)
1761 : {
1762 : /*
1763 : * Unlike the case of list partitioning, we wouldn't have re-merged
1764 : * partitions, so did_remapping should be left alone.
1765 : */
1766 : Assert(!outer_map.did_remapping);
1767 : Assert(!inner_map.did_remapping);
1768 :
1769 : /* Use maps to match partitions from inputs. */
1770 294 : generate_matching_part_pairs(outer_rel, inner_rel,
1771 : &outer_map, &inner_map,
1772 : next_index,
1773 : outer_parts, inner_parts);
1774 : Assert(*outer_parts != NIL);
1775 : Assert(*inner_parts != NIL);
1776 : Assert(list_length(*outer_parts) == list_length(*inner_parts));
1777 : Assert(list_length(*outer_parts) == next_index);
1778 :
1779 : /* Make a PartitionBoundInfo struct to return. */
1780 294 : merged_bounds = build_merged_partition_bounds(outer_bi->strategy,
1781 : merged_datums,
1782 : merged_kinds,
1783 : merged_indexes,
1784 : -1,
1785 : default_index);
1786 : Assert(merged_bounds);
1787 : }
1788 :
1789 0 : cleanup:
1790 : /* Free local memory before returning. */
1791 360 : list_free(merged_datums);
1792 360 : list_free(merged_kinds);
1793 360 : list_free(merged_indexes);
1794 360 : free_partition_map(&outer_map);
1795 360 : free_partition_map(&inner_map);
1796 :
1797 360 : return merged_bounds;
1798 : }
1799 :
1800 : /*
1801 : * init_partition_map
1802 : * Initialize a PartitionMap struct for given relation
1803 : */
1804 : static void
1805 1692 : init_partition_map(RelOptInfo *rel, PartitionMap *map)
1806 : {
1807 1692 : int nparts = rel->nparts;
1808 : int i;
1809 :
1810 1692 : map->nparts = nparts;
1811 1692 : map->merged_indexes = palloc_array(int, nparts);
1812 1692 : map->merged = palloc_array(bool, nparts);
1813 1692 : map->did_remapping = false;
1814 1692 : map->old_indexes = palloc_array(int, nparts);
1815 6870 : for (i = 0; i < nparts; i++)
1816 : {
1817 5178 : map->merged_indexes[i] = map->old_indexes[i] = -1;
1818 5178 : map->merged[i] = false;
1819 : }
1820 1692 : }
1821 :
1822 : /*
1823 : * free_partition_map
1824 : */
1825 : static void
1826 1692 : free_partition_map(PartitionMap *map)
1827 : {
1828 1692 : pfree(map->merged_indexes);
1829 1692 : pfree(map->merged);
1830 1692 : pfree(map->old_indexes);
1831 1692 : }
1832 :
1833 : /*
1834 : * is_dummy_partition --- has partition been proven empty?
1835 : */
1836 : static bool
1837 9144 : is_dummy_partition(RelOptInfo *rel, int part_index)
1838 : {
1839 : RelOptInfo *part_rel;
1840 :
1841 : Assert(part_index >= 0);
1842 9144 : part_rel = rel->part_rels[part_index];
1843 9144 : if (part_rel == NULL || IS_DUMMY_REL(part_rel))
1844 252 : return true;
1845 8892 : return false;
1846 : }
1847 :
1848 : /*
1849 : * merge_matching_partitions
1850 : * Try to merge given outer/inner partitions, and return the index of a
1851 : * merged partition produced from them if successful, -1 otherwise
1852 : *
1853 : * If the merged partition is newly created, *next_index is incremented.
1854 : */
1855 : static int
1856 2718 : merge_matching_partitions(PartitionMap *outer_map, PartitionMap *inner_map,
1857 : int outer_index, int inner_index, int *next_index)
1858 : {
1859 : int outer_merged_index;
1860 : int inner_merged_index;
1861 : bool outer_merged;
1862 : bool inner_merged;
1863 :
1864 : Assert(outer_index >= 0 && outer_index < outer_map->nparts);
1865 2718 : outer_merged_index = outer_map->merged_indexes[outer_index];
1866 2718 : outer_merged = outer_map->merged[outer_index];
1867 : Assert(inner_index >= 0 && inner_index < inner_map->nparts);
1868 2718 : inner_merged_index = inner_map->merged_indexes[inner_index];
1869 2718 : inner_merged = inner_map->merged[inner_index];
1870 :
1871 : /*
1872 : * Handle cases where we have already assigned a merged partition to each
1873 : * of the given partitions.
1874 : */
1875 2718 : if (outer_merged_index >= 0 && inner_merged_index >= 0)
1876 : {
1877 : /*
1878 : * If the merged partitions are the same, no need to do anything;
1879 : * return the index of the merged partitions. Otherwise, if each of
1880 : * the given partitions has been merged with a dummy partition on the
1881 : * other side, re-map them to either of the two merged partitions.
1882 : * Otherwise, they can't be merged, so return -1.
1883 : */
1884 660 : if (outer_merged_index == inner_merged_index)
1885 : {
1886 : Assert(outer_merged);
1887 : Assert(inner_merged);
1888 552 : return outer_merged_index;
1889 : }
1890 108 : if (!outer_merged && !inner_merged)
1891 : {
1892 : /*
1893 : * This can only happen for a list-partitioning case. We re-map
1894 : * them to the merged partition with the smaller of the two merged
1895 : * indexes to preserve the property that the canonical order of
1896 : * list partitions is determined by the indexes assigned to the
1897 : * smallest list value of each partition.
1898 : */
1899 102 : if (outer_merged_index < inner_merged_index)
1900 : {
1901 54 : outer_map->merged[outer_index] = true;
1902 54 : inner_map->merged_indexes[inner_index] = outer_merged_index;
1903 54 : inner_map->merged[inner_index] = true;
1904 54 : inner_map->did_remapping = true;
1905 54 : inner_map->old_indexes[inner_index] = inner_merged_index;
1906 54 : return outer_merged_index;
1907 : }
1908 : else
1909 : {
1910 48 : inner_map->merged[inner_index] = true;
1911 48 : outer_map->merged_indexes[outer_index] = inner_merged_index;
1912 48 : outer_map->merged[outer_index] = true;
1913 48 : outer_map->did_remapping = true;
1914 48 : outer_map->old_indexes[outer_index] = outer_merged_index;
1915 48 : return inner_merged_index;
1916 : }
1917 : }
1918 6 : return -1;
1919 : }
1920 :
1921 : /* At least one of the given partitions should not have yet been merged. */
1922 : Assert(outer_merged_index == -1 || inner_merged_index == -1);
1923 :
1924 : /*
1925 : * If neither of them has been merged, merge them. Otherwise, if one has
1926 : * been merged with a dummy partition on the other side (and the other
1927 : * hasn't yet been merged with anything), re-merge them. Otherwise, they
1928 : * can't be merged, so return -1.
1929 : */
1930 2058 : if (outer_merged_index == -1 && inner_merged_index == -1)
1931 : {
1932 1758 : int merged_index = *next_index;
1933 :
1934 : Assert(!outer_merged);
1935 : Assert(!inner_merged);
1936 1758 : outer_map->merged_indexes[outer_index] = merged_index;
1937 1758 : outer_map->merged[outer_index] = true;
1938 1758 : inner_map->merged_indexes[inner_index] = merged_index;
1939 1758 : inner_map->merged[inner_index] = true;
1940 1758 : *next_index = *next_index + 1;
1941 1758 : return merged_index;
1942 : }
1943 300 : if (outer_merged_index >= 0 && !outer_map->merged[outer_index])
1944 : {
1945 : Assert(inner_merged_index == -1);
1946 : Assert(!inner_merged);
1947 264 : inner_map->merged_indexes[inner_index] = outer_merged_index;
1948 264 : inner_map->merged[inner_index] = true;
1949 264 : outer_map->merged[outer_index] = true;
1950 264 : return outer_merged_index;
1951 : }
1952 36 : if (inner_merged_index >= 0 && !inner_map->merged[inner_index])
1953 : {
1954 : Assert(outer_merged_index == -1);
1955 : Assert(!outer_merged);
1956 0 : outer_map->merged_indexes[outer_index] = inner_merged_index;
1957 0 : outer_map->merged[outer_index] = true;
1958 0 : inner_map->merged[inner_index] = true;
1959 0 : return inner_merged_index;
1960 : }
1961 36 : return -1;
1962 : }
1963 :
1964 : /*
1965 : * process_outer_partition
1966 : * Try to assign given outer partition a merged partition, and return the
1967 : * index of the merged partition if successful, -1 otherwise
1968 : *
1969 : * If the partition is newly created, *next_index is incremented. Also, if it
1970 : * is the default partition of the join relation, *default_index is set to the
1971 : * index if not already done.
1972 : */
1973 : static int
1974 432 : process_outer_partition(PartitionMap *outer_map,
1975 : PartitionMap *inner_map,
1976 : bool outer_has_default,
1977 : bool inner_has_default,
1978 : int outer_index,
1979 : int inner_default,
1980 : JoinType jointype,
1981 : int *next_index,
1982 : int *default_index)
1983 : {
1984 432 : int merged_index = -1;
1985 :
1986 : Assert(outer_index >= 0);
1987 :
1988 : /*
1989 : * If the inner side has the default partition, a row from the outer
1990 : * partition might find its join partner in the default partition; try
1991 : * merging the outer partition with the default partition. Otherwise,
1992 : * this should be an outer join, in which case the outer partition has to
1993 : * be scanned all the way anyway; merge the outer partition with a dummy
1994 : * partition on the other side.
1995 : */
1996 432 : if (inner_has_default)
1997 : {
1998 : Assert(inner_default >= 0);
1999 :
2000 : /*
2001 : * If the outer side has the default partition as well, the default
2002 : * partition on the inner side will have two matching partitions on
2003 : * the other side: the outer partition and the default partition on
2004 : * the outer side. Partitionwise join doesn't handle this scenario
2005 : * yet.
2006 : */
2007 6 : if (outer_has_default)
2008 0 : return -1;
2009 :
2010 6 : merged_index = merge_matching_partitions(outer_map, inner_map,
2011 : outer_index, inner_default,
2012 : next_index);
2013 6 : if (merged_index == -1)
2014 6 : return -1;
2015 :
2016 : /*
2017 : * If this is a FULL join, the default partition on the inner side has
2018 : * to be scanned all the way anyway, so the resulting partition will
2019 : * contain all key values from the default partition, which any other
2020 : * partition of the join relation will not contain. Thus the
2021 : * resulting partition will act as the default partition of the join
2022 : * relation; record the index in *default_index if not already done.
2023 : */
2024 0 : if (jointype == JOIN_FULL)
2025 : {
2026 0 : if (*default_index == -1)
2027 0 : *default_index = merged_index;
2028 : else
2029 : Assert(*default_index == merged_index);
2030 : }
2031 : }
2032 : else
2033 : {
2034 : Assert(IS_OUTER_JOIN(jointype));
2035 : Assert(jointype != JOIN_RIGHT);
2036 :
2037 : /* If we have already assigned a partition, no need to do anything. */
2038 426 : merged_index = outer_map->merged_indexes[outer_index];
2039 426 : if (merged_index == -1)
2040 402 : merged_index = merge_partition_with_dummy(outer_map, outer_index,
2041 : next_index);
2042 : }
2043 426 : return merged_index;
2044 : }
2045 :
2046 : /*
2047 : * process_inner_partition
2048 : * Try to assign given inner partition a merged partition, and return the
2049 : * index of the merged partition if successful, -1 otherwise
2050 : *
2051 : * If the partition is newly created, *next_index is incremented. Also, if it
2052 : * is the default partition of the join relation, *default_index is set to the
2053 : * index if not already done.
2054 : */
2055 : static int
2056 318 : process_inner_partition(PartitionMap *outer_map,
2057 : PartitionMap *inner_map,
2058 : bool outer_has_default,
2059 : bool inner_has_default,
2060 : int inner_index,
2061 : int outer_default,
2062 : JoinType jointype,
2063 : int *next_index,
2064 : int *default_index)
2065 : {
2066 318 : int merged_index = -1;
2067 :
2068 : Assert(inner_index >= 0);
2069 :
2070 : /*
2071 : * If the outer side has the default partition, a row from the inner
2072 : * partition might find its join partner in the default partition; try
2073 : * merging the inner partition with the default partition. Otherwise,
2074 : * this should be a FULL join, in which case the inner partition has to be
2075 : * scanned all the way anyway; merge the inner partition with a dummy
2076 : * partition on the other side.
2077 : */
2078 318 : if (outer_has_default)
2079 : {
2080 : Assert(outer_default >= 0);
2081 :
2082 : /*
2083 : * If the inner side has the default partition as well, the default
2084 : * partition on the outer side will have two matching partitions on
2085 : * the other side: the inner partition and the default partition on
2086 : * the inner side. Partitionwise join doesn't handle this scenario
2087 : * yet.
2088 : */
2089 204 : if (inner_has_default)
2090 12 : return -1;
2091 :
2092 192 : merged_index = merge_matching_partitions(outer_map, inner_map,
2093 : outer_default, inner_index,
2094 : next_index);
2095 192 : if (merged_index == -1)
2096 6 : return -1;
2097 :
2098 : /*
2099 : * If this is an outer join, the default partition on the outer side
2100 : * has to be scanned all the way anyway, so the resulting partition
2101 : * will contain all key values from the default partition, which any
2102 : * other partition of the join relation will not contain. Thus the
2103 : * resulting partition will act as the default partition of the join
2104 : * relation; record the index in *default_index if not already done.
2105 : */
2106 186 : if (IS_OUTER_JOIN(jointype))
2107 : {
2108 : Assert(jointype != JOIN_RIGHT);
2109 108 : if (*default_index == -1)
2110 72 : *default_index = merged_index;
2111 : else
2112 : Assert(*default_index == merged_index);
2113 : }
2114 : }
2115 : else
2116 : {
2117 : Assert(jointype == JOIN_FULL);
2118 :
2119 : /* If we have already assigned a partition, no need to do anything. */
2120 114 : merged_index = inner_map->merged_indexes[inner_index];
2121 114 : if (merged_index == -1)
2122 114 : merged_index = merge_partition_with_dummy(inner_map, inner_index,
2123 : next_index);
2124 : }
2125 300 : return merged_index;
2126 : }
2127 :
2128 : /*
2129 : * merge_null_partitions
2130 : * Merge the NULL partitions from a join's outer and inner sides.
2131 : *
2132 : * If the merged partition produced from them is the NULL partition of the join
2133 : * relation, *null_index is set to the index of the merged partition.
2134 : *
2135 : * Note: We assume here that the join clause for a partitioned join is strict
2136 : * because have_partkey_equi_join() requires that the corresponding operator
2137 : * be mergejoinable, and we currently assume that mergejoinable operators are
2138 : * strict (see MJEvalOuterValues()/MJEvalInnerValues()).
2139 : */
2140 : static void
2141 216 : merge_null_partitions(PartitionMap *outer_map,
2142 : PartitionMap *inner_map,
2143 : bool outer_has_null,
2144 : bool inner_has_null,
2145 : int outer_null,
2146 : int inner_null,
2147 : JoinType jointype,
2148 : int *next_index,
2149 : int *null_index)
2150 : {
2151 216 : bool consider_outer_null = false;
2152 216 : bool consider_inner_null = false;
2153 :
2154 : Assert(outer_has_null || inner_has_null);
2155 : Assert(*null_index == -1);
2156 :
2157 : /*
2158 : * Check whether the NULL partitions have already been merged and if so,
2159 : * set the consider_outer_null/consider_inner_null flags.
2160 : */
2161 216 : if (outer_has_null)
2162 : {
2163 : Assert(outer_null >= 0 && outer_null < outer_map->nparts);
2164 192 : if (outer_map->merged_indexes[outer_null] == -1)
2165 84 : consider_outer_null = true;
2166 : }
2167 216 : if (inner_has_null)
2168 : {
2169 : Assert(inner_null >= 0 && inner_null < inner_map->nparts);
2170 192 : if (inner_map->merged_indexes[inner_null] == -1)
2171 120 : consider_inner_null = true;
2172 : }
2173 :
2174 : /* If both flags are set false, we don't need to do anything. */
2175 216 : if (!consider_outer_null && !consider_inner_null)
2176 72 : return;
2177 :
2178 144 : if (consider_outer_null && !consider_inner_null)
2179 : {
2180 : Assert(outer_has_null);
2181 :
2182 : /*
2183 : * If this is an outer join, the NULL partition on the outer side has
2184 : * to be scanned all the way anyway; merge the NULL partition with a
2185 : * dummy partition on the other side. In that case
2186 : * consider_outer_null means that the NULL partition only contains
2187 : * NULL values as the key values, so the merged partition will do so;
2188 : * treat it as the NULL partition of the join relation.
2189 : */
2190 24 : if (IS_OUTER_JOIN(jointype))
2191 : {
2192 : Assert(jointype != JOIN_RIGHT);
2193 12 : *null_index = merge_partition_with_dummy(outer_map, outer_null,
2194 : next_index);
2195 : }
2196 : }
2197 120 : else if (!consider_outer_null && consider_inner_null)
2198 : {
2199 : Assert(inner_has_null);
2200 :
2201 : /*
2202 : * If this is a FULL join, the NULL partition on the inner side has to
2203 : * be scanned all the way anyway; merge the NULL partition with a
2204 : * dummy partition on the other side. In that case
2205 : * consider_inner_null means that the NULL partition only contains
2206 : * NULL values as the key values, so the merged partition will do so;
2207 : * treat it as the NULL partition of the join relation.
2208 : */
2209 60 : if (jointype == JOIN_FULL)
2210 0 : *null_index = merge_partition_with_dummy(inner_map, inner_null,
2211 : next_index);
2212 : }
2213 : else
2214 : {
2215 : Assert(consider_outer_null && consider_inner_null);
2216 : Assert(outer_has_null);
2217 : Assert(inner_has_null);
2218 :
2219 : /*
2220 : * If this is an outer join, the NULL partition on the outer side (and
2221 : * that on the inner side if this is a FULL join) have to be scanned
2222 : * all the way anyway, so merge them. Note that each of the NULL
2223 : * partitions isn't merged yet, so they should be merged successfully.
2224 : * Like the above, each of the NULL partitions only contains NULL
2225 : * values as the key values, so the merged partition will do so; treat
2226 : * it as the NULL partition of the join relation.
2227 : *
2228 : * Note: if this an INNER/SEMI join, the join clause will never be
2229 : * satisfied by two NULL values (see comments above), so both the NULL
2230 : * partitions can be eliminated.
2231 : */
2232 60 : if (IS_OUTER_JOIN(jointype))
2233 : {
2234 : Assert(jointype != JOIN_RIGHT);
2235 48 : *null_index = merge_matching_partitions(outer_map, inner_map,
2236 : outer_null, inner_null,
2237 : next_index);
2238 : Assert(*null_index >= 0);
2239 : }
2240 : }
2241 : }
2242 :
2243 : /*
2244 : * merge_default_partitions
2245 : * Merge the default partitions from a join's outer and inner sides.
2246 : *
2247 : * If the merged partition produced from them is the default partition of the
2248 : * join relation, *default_index is set to the index of the merged partition.
2249 : */
2250 : static void
2251 156 : merge_default_partitions(PartitionMap *outer_map,
2252 : PartitionMap *inner_map,
2253 : bool outer_has_default,
2254 : bool inner_has_default,
2255 : int outer_default,
2256 : int inner_default,
2257 : JoinType jointype,
2258 : int *next_index,
2259 : int *default_index)
2260 : {
2261 156 : int outer_merged_index = -1;
2262 156 : int inner_merged_index = -1;
2263 :
2264 : Assert(outer_has_default || inner_has_default);
2265 :
2266 : /* Get the merged partition indexes for the default partitions. */
2267 156 : if (outer_has_default)
2268 : {
2269 : Assert(outer_default >= 0 && outer_default < outer_map->nparts);
2270 120 : outer_merged_index = outer_map->merged_indexes[outer_default];
2271 : }
2272 156 : if (inner_has_default)
2273 : {
2274 : Assert(inner_default >= 0 && inner_default < inner_map->nparts);
2275 36 : inner_merged_index = inner_map->merged_indexes[inner_default];
2276 : }
2277 :
2278 156 : if (outer_has_default && !inner_has_default)
2279 : {
2280 : /*
2281 : * If this is an outer join, the default partition on the outer side
2282 : * has to be scanned all the way anyway; if we have not yet assigned a
2283 : * partition, merge the default partition with a dummy partition on
2284 : * the other side. The merged partition will act as the default
2285 : * partition of the join relation (see comments in
2286 : * process_inner_partition()).
2287 : */
2288 120 : if (IS_OUTER_JOIN(jointype))
2289 : {
2290 : Assert(jointype != JOIN_RIGHT);
2291 72 : if (outer_merged_index == -1)
2292 : {
2293 : Assert(*default_index == -1);
2294 0 : *default_index = merge_partition_with_dummy(outer_map,
2295 : outer_default,
2296 : next_index);
2297 : }
2298 : else
2299 : Assert(*default_index == outer_merged_index);
2300 : }
2301 : else
2302 : Assert(*default_index == -1);
2303 : }
2304 36 : else if (!outer_has_default && inner_has_default)
2305 : {
2306 : /*
2307 : * If this is a FULL join, the default partition on the inner side has
2308 : * to be scanned all the way anyway; if we have not yet assigned a
2309 : * partition, merge the default partition with a dummy partition on
2310 : * the other side. The merged partition will act as the default
2311 : * partition of the join relation (see comments in
2312 : * process_outer_partition()).
2313 : */
2314 36 : if (jointype == JOIN_FULL)
2315 : {
2316 0 : if (inner_merged_index == -1)
2317 : {
2318 : Assert(*default_index == -1);
2319 0 : *default_index = merge_partition_with_dummy(inner_map,
2320 : inner_default,
2321 : next_index);
2322 : }
2323 : else
2324 : Assert(*default_index == inner_merged_index);
2325 : }
2326 : else
2327 : Assert(*default_index == -1);
2328 : }
2329 : else
2330 : {
2331 : Assert(outer_has_default && inner_has_default);
2332 :
2333 : /*
2334 : * The default partitions have to be joined with each other, so merge
2335 : * them. Note that each of the default partitions isn't merged yet
2336 : * (see, process_outer_partition()/process_inner_partition()), so they
2337 : * should be merged successfully. The merged partition will act as
2338 : * the default partition of the join relation.
2339 : */
2340 : Assert(outer_merged_index == -1);
2341 : Assert(inner_merged_index == -1);
2342 : Assert(*default_index == -1);
2343 0 : *default_index = merge_matching_partitions(outer_map,
2344 : inner_map,
2345 : outer_default,
2346 : inner_default,
2347 : next_index);
2348 : Assert(*default_index >= 0);
2349 : }
2350 156 : }
2351 :
2352 : /*
2353 : * merge_partition_with_dummy
2354 : * Assign given partition a new partition of a join relation
2355 : *
2356 : * Note: The caller assumes that the given partition doesn't have a non-dummy
2357 : * matching partition on the other side, but if the given partition finds the
2358 : * matching partition later, we will adjust the assignment.
2359 : */
2360 : static int
2361 528 : merge_partition_with_dummy(PartitionMap *map, int index, int *next_index)
2362 : {
2363 528 : int merged_index = *next_index;
2364 :
2365 : Assert(index >= 0 && index < map->nparts);
2366 : Assert(map->merged_indexes[index] == -1);
2367 : Assert(!map->merged[index]);
2368 528 : map->merged_indexes[index] = merged_index;
2369 : /* Leave the merged flag alone! */
2370 528 : *next_index = *next_index + 1;
2371 528 : return merged_index;
2372 : }
2373 :
2374 : /*
2375 : * fix_merged_indexes
2376 : * Adjust merged indexes of re-merged partitions
2377 : */
2378 : static void
2379 48 : fix_merged_indexes(PartitionMap *outer_map, PartitionMap *inner_map,
2380 : int nmerged, List *merged_indexes)
2381 : {
2382 : int *new_indexes;
2383 : int merged_index;
2384 : int i;
2385 : ListCell *lc;
2386 :
2387 : Assert(nmerged > 0);
2388 :
2389 48 : new_indexes = palloc_array(int, nmerged);
2390 312 : for (i = 0; i < nmerged; i++)
2391 264 : new_indexes[i] = -1;
2392 :
2393 : /* Build the mapping of old merged indexes to new merged indexes. */
2394 48 : if (outer_map->did_remapping)
2395 : {
2396 210 : for (i = 0; i < outer_map->nparts; i++)
2397 : {
2398 162 : merged_index = outer_map->old_indexes[i];
2399 162 : if (merged_index >= 0)
2400 48 : new_indexes[merged_index] = outer_map->merged_indexes[i];
2401 : }
2402 : }
2403 48 : if (inner_map->did_remapping)
2404 : {
2405 210 : for (i = 0; i < inner_map->nparts; i++)
2406 : {
2407 162 : merged_index = inner_map->old_indexes[i];
2408 162 : if (merged_index >= 0)
2409 48 : new_indexes[merged_index] = inner_map->merged_indexes[i];
2410 : }
2411 : }
2412 :
2413 : /* Fix the merged_indexes list using the mapping. */
2414 438 : foreach(lc, merged_indexes)
2415 : {
2416 390 : merged_index = lfirst_int(lc);
2417 : Assert(merged_index >= 0);
2418 390 : if (new_indexes[merged_index] >= 0)
2419 96 : lfirst_int(lc) = new_indexes[merged_index];
2420 : }
2421 :
2422 48 : pfree(new_indexes);
2423 48 : }
2424 :
2425 : /*
2426 : * generate_matching_part_pairs
2427 : * Generate a pair of lists of partitions that produce merged partitions
2428 : *
2429 : * The lists of partitions are built in the order of merged partition indexes,
2430 : * and returned in *outer_parts and *inner_parts.
2431 : */
2432 : static void
2433 732 : generate_matching_part_pairs(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
2434 : PartitionMap *outer_map, PartitionMap *inner_map,
2435 : int nmerged,
2436 : List **outer_parts, List **inner_parts)
2437 : {
2438 732 : int outer_nparts = outer_map->nparts;
2439 732 : int inner_nparts = inner_map->nparts;
2440 : int *outer_indexes;
2441 : int *inner_indexes;
2442 : int max_nparts;
2443 : int i;
2444 :
2445 : Assert(nmerged > 0);
2446 : Assert(*outer_parts == NIL);
2447 : Assert(*inner_parts == NIL);
2448 :
2449 732 : outer_indexes = palloc_array(int, nmerged);
2450 732 : inner_indexes = palloc_array(int, nmerged);
2451 2796 : for (i = 0; i < nmerged; i++)
2452 2064 : outer_indexes[i] = inner_indexes[i] = -1;
2453 :
2454 : /* Set pairs of matching partitions. */
2455 : Assert(outer_nparts == outer_rel->nparts);
2456 : Assert(inner_nparts == inner_rel->nparts);
2457 732 : max_nparts = Max(outer_nparts, inner_nparts);
2458 3060 : for (i = 0; i < max_nparts; i++)
2459 : {
2460 2328 : if (i < outer_nparts)
2461 : {
2462 2220 : int merged_index = outer_map->merged_indexes[i];
2463 :
2464 2220 : if (merged_index >= 0)
2465 : {
2466 : Assert(merged_index < nmerged);
2467 1956 : outer_indexes[merged_index] = i;
2468 : }
2469 : }
2470 2328 : if (i < inner_nparts)
2471 : {
2472 2244 : int merged_index = inner_map->merged_indexes[i];
2473 :
2474 2244 : if (merged_index >= 0)
2475 : {
2476 : Assert(merged_index < nmerged);
2477 1920 : inner_indexes[merged_index] = i;
2478 : }
2479 : }
2480 : }
2481 :
2482 : /* Build the list pairs. */
2483 2796 : for (i = 0; i < nmerged; i++)
2484 : {
2485 2064 : int outer_index = outer_indexes[i];
2486 2064 : int inner_index = inner_indexes[i];
2487 :
2488 : /*
2489 : * If both partitions are dummy, it means the merged partition that
2490 : * had been assigned to the outer/inner partition was removed when
2491 : * re-merging the outer/inner partition in
2492 : * merge_matching_partitions(); ignore the merged partition.
2493 : */
2494 2064 : if (outer_index == -1 && inner_index == -1)
2495 96 : continue;
2496 :
2497 3924 : *outer_parts = lappend(*outer_parts, outer_index >= 0 ?
2498 1956 : outer_rel->part_rels[outer_index] : NULL);
2499 3888 : *inner_parts = lappend(*inner_parts, inner_index >= 0 ?
2500 1920 : inner_rel->part_rels[inner_index] : NULL);
2501 : }
2502 :
2503 732 : pfree(outer_indexes);
2504 732 : pfree(inner_indexes);
2505 732 : }
2506 :
2507 : /*
2508 : * build_merged_partition_bounds
2509 : * Create a PartitionBoundInfo struct from merged partition bounds
2510 : */
2511 : static PartitionBoundInfo
2512 732 : build_merged_partition_bounds(char strategy, List *merged_datums,
2513 : List *merged_kinds, List *merged_indexes,
2514 : int null_index, int default_index)
2515 : {
2516 : PartitionBoundInfo merged_bounds;
2517 732 : int ndatums = list_length(merged_datums);
2518 : int pos;
2519 : ListCell *lc;
2520 :
2521 732 : merged_bounds = palloc_object(PartitionBoundInfoData);
2522 732 : merged_bounds->strategy = strategy;
2523 732 : merged_bounds->ndatums = ndatums;
2524 :
2525 732 : merged_bounds->datums = palloc_array(Datum *, ndatums);
2526 732 : pos = 0;
2527 4044 : foreach(lc, merged_datums)
2528 3312 : merged_bounds->datums[pos++] = (Datum *) lfirst(lc);
2529 :
2530 732 : if (strategy == PARTITION_STRATEGY_RANGE)
2531 : {
2532 : Assert(list_length(merged_kinds) == ndatums);
2533 294 : merged_bounds->kind = palloc_array(PartitionRangeDatumKind *, ndatums);
2534 294 : pos = 0;
2535 1560 : foreach(lc, merged_kinds)
2536 1266 : merged_bounds->kind[pos++] = (PartitionRangeDatumKind *) lfirst(lc);
2537 :
2538 : /* There are ndatums+1 indexes in the case of range partitioning. */
2539 294 : merged_indexes = lappend_int(merged_indexes, -1);
2540 294 : ndatums++;
2541 : }
2542 : else
2543 : {
2544 : Assert(strategy == PARTITION_STRATEGY_LIST);
2545 : Assert(merged_kinds == NIL);
2546 438 : merged_bounds->kind = NULL;
2547 : }
2548 :
2549 : /* interleaved_parts is always NULL for join relations. */
2550 732 : merged_bounds->interleaved_parts = NULL;
2551 :
2552 : Assert(list_length(merged_indexes) == ndatums);
2553 732 : merged_bounds->nindexes = ndatums;
2554 732 : merged_bounds->indexes = palloc_array(int, ndatums);
2555 732 : pos = 0;
2556 4338 : foreach(lc, merged_indexes)
2557 3606 : merged_bounds->indexes[pos++] = lfirst_int(lc);
2558 :
2559 732 : merged_bounds->null_index = null_index;
2560 732 : merged_bounds->default_index = default_index;
2561 :
2562 732 : return merged_bounds;
2563 : }
2564 :
2565 : /*
2566 : * get_range_partition
2567 : * Get the next non-dummy partition of a range-partitioned relation,
2568 : * returning the index of that partition
2569 : *
2570 : * *lb and *ub are set to the lower and upper bounds of that partition
2571 : * respectively, and *lb_pos is advanced to the next lower bound, if any.
2572 : */
2573 : static int
2574 2532 : get_range_partition(RelOptInfo *rel,
2575 : PartitionBoundInfo bi,
2576 : int *lb_pos,
2577 : PartitionRangeBound *lb,
2578 : PartitionRangeBound *ub)
2579 : {
2580 : int part_index;
2581 :
2582 : Assert(bi->strategy == PARTITION_STRATEGY_RANGE);
2583 :
2584 : do
2585 : {
2586 2580 : part_index = get_range_partition_internal(bi, lb_pos, lb, ub);
2587 2580 : if (part_index == -1)
2588 630 : return -1;
2589 1950 : } while (is_dummy_partition(rel, part_index));
2590 :
2591 1902 : return part_index;
2592 : }
2593 :
2594 : static int
2595 2580 : get_range_partition_internal(PartitionBoundInfo bi,
2596 : int *lb_pos,
2597 : PartitionRangeBound *lb,
2598 : PartitionRangeBound *ub)
2599 : {
2600 : /* Return the index as -1 if we've exhausted all lower bounds. */
2601 2580 : if (*lb_pos >= bi->ndatums)
2602 630 : return -1;
2603 :
2604 : /* A lower bound should have at least one more bound after it. */
2605 : Assert(*lb_pos + 1 < bi->ndatums);
2606 :
2607 : /* Set the lower bound. */
2608 1950 : lb->index = bi->indexes[*lb_pos];
2609 1950 : lb->datums = bi->datums[*lb_pos];
2610 1950 : lb->kind = bi->kind[*lb_pos];
2611 1950 : lb->lower = true;
2612 : /* Set the upper bound. */
2613 1950 : ub->index = bi->indexes[*lb_pos + 1];
2614 1950 : ub->datums = bi->datums[*lb_pos + 1];
2615 1950 : ub->kind = bi->kind[*lb_pos + 1];
2616 1950 : ub->lower = false;
2617 :
2618 : /* The index assigned to an upper bound should be valid. */
2619 : Assert(ub->index >= 0);
2620 :
2621 : /*
2622 : * Advance the position to the next lower bound. If there are no bounds
2623 : * left beyond the upper bound, we have reached the last lower bound.
2624 : */
2625 1950 : if (*lb_pos + 2 >= bi->ndatums)
2626 684 : *lb_pos = bi->ndatums;
2627 : else
2628 : {
2629 : /*
2630 : * If the index assigned to the bound next to the upper bound isn't
2631 : * valid, that is the next lower bound; else, the upper bound is also
2632 : * the lower bound of the next range partition.
2633 : */
2634 1266 : if (bi->indexes[*lb_pos + 2] < 0)
2635 474 : *lb_pos = *lb_pos + 2;
2636 : else
2637 792 : *lb_pos = *lb_pos + 1;
2638 : }
2639 :
2640 1950 : return ub->index;
2641 : }
2642 :
2643 : /*
2644 : * compare_range_partitions
2645 : * Compare the bounds of two range partitions, and return true if the
2646 : * two partitions overlap, false otherwise
2647 : *
2648 : * *lb_cmpval is set to -1, 0, or 1 if the outer partition's lower bound is
2649 : * lower than, equal to, or higher than the inner partition's lower bound
2650 : * respectively. Likewise, *ub_cmpval is set to -1, 0, or 1 if the outer
2651 : * partition's upper bound is lower than, equal to, or higher than the inner
2652 : * partition's upper bound respectively.
2653 : */
2654 : static bool
2655 864 : compare_range_partitions(int partnatts, FmgrInfo *partsupfuncs,
2656 : Oid *partcollations,
2657 : PartitionRangeBound *outer_lb,
2658 : PartitionRangeBound *outer_ub,
2659 : PartitionRangeBound *inner_lb,
2660 : PartitionRangeBound *inner_ub,
2661 : int *lb_cmpval, int *ub_cmpval)
2662 : {
2663 : /*
2664 : * Check if the outer partition's upper bound is lower than the inner
2665 : * partition's lower bound; if so the partitions aren't overlapping.
2666 : */
2667 864 : if (compare_range_bounds(partnatts, partsupfuncs, partcollations,
2668 : outer_ub, inner_lb) < 0)
2669 : {
2670 0 : *lb_cmpval = -1;
2671 0 : *ub_cmpval = -1;
2672 0 : return false;
2673 : }
2674 :
2675 : /*
2676 : * Check if the outer partition's lower bound is higher than the inner
2677 : * partition's upper bound; if so the partitions aren't overlapping.
2678 : */
2679 864 : if (compare_range_bounds(partnatts, partsupfuncs, partcollations,
2680 : outer_lb, inner_ub) > 0)
2681 : {
2682 36 : *lb_cmpval = 1;
2683 36 : *ub_cmpval = 1;
2684 36 : return false;
2685 : }
2686 :
2687 : /* All other cases indicate overlapping partitions. */
2688 828 : *lb_cmpval = compare_range_bounds(partnatts, partsupfuncs, partcollations,
2689 : outer_lb, inner_lb);
2690 828 : *ub_cmpval = compare_range_bounds(partnatts, partsupfuncs, partcollations,
2691 : outer_ub, inner_ub);
2692 828 : return true;
2693 : }
2694 :
2695 : /*
2696 : * get_merged_range_bounds
2697 : * Given the bounds of range partitions to be joined, determine the bounds
2698 : * of a merged partition produced from the range partitions
2699 : *
2700 : * *merged_lb and *merged_ub are set to the lower and upper bounds of the
2701 : * merged partition.
2702 : */
2703 : static void
2704 828 : get_merged_range_bounds(int partnatts, FmgrInfo *partsupfuncs,
2705 : Oid *partcollations, JoinType jointype,
2706 : PartitionRangeBound *outer_lb,
2707 : PartitionRangeBound *outer_ub,
2708 : PartitionRangeBound *inner_lb,
2709 : PartitionRangeBound *inner_ub,
2710 : int lb_cmpval, int ub_cmpval,
2711 : PartitionRangeBound *merged_lb,
2712 : PartitionRangeBound *merged_ub)
2713 : {
2714 : Assert(compare_range_bounds(partnatts, partsupfuncs, partcollations,
2715 : outer_lb, inner_lb) == lb_cmpval);
2716 : Assert(compare_range_bounds(partnatts, partsupfuncs, partcollations,
2717 : outer_ub, inner_ub) == ub_cmpval);
2718 :
2719 828 : switch (jointype)
2720 : {
2721 432 : case JOIN_INNER:
2722 : case JOIN_SEMI:
2723 :
2724 : /*
2725 : * An INNER/SEMI join will have the rows that fit both sides, so
2726 : * the lower bound of the merged partition will be the higher of
2727 : * the two lower bounds, and the upper bound of the merged
2728 : * partition will be the lower of the two upper bounds.
2729 : */
2730 432 : *merged_lb = (lb_cmpval > 0) ? *outer_lb : *inner_lb;
2731 432 : *merged_ub = (ub_cmpval < 0) ? *outer_ub : *inner_ub;
2732 432 : break;
2733 :
2734 324 : case JOIN_LEFT:
2735 : case JOIN_ANTI:
2736 :
2737 : /*
2738 : * A LEFT/ANTI join will have all the rows from the outer side, so
2739 : * the bounds of the merged partition will be the same as the
2740 : * outer bounds.
2741 : */
2742 324 : *merged_lb = *outer_lb;
2743 324 : *merged_ub = *outer_ub;
2744 324 : break;
2745 :
2746 72 : case JOIN_FULL:
2747 :
2748 : /*
2749 : * A FULL join will have all the rows from both sides, so the
2750 : * lower bound of the merged partition will be the lower of the
2751 : * two lower bounds, and the upper bound of the merged partition
2752 : * will be the higher of the two upper bounds.
2753 : */
2754 72 : *merged_lb = (lb_cmpval < 0) ? *outer_lb : *inner_lb;
2755 72 : *merged_ub = (ub_cmpval > 0) ? *outer_ub : *inner_ub;
2756 72 : break;
2757 :
2758 0 : default:
2759 0 : elog(ERROR, "unrecognized join type: %d", (int) jointype);
2760 : }
2761 828 : }
2762 :
2763 : /*
2764 : * add_merged_range_bounds
2765 : * Add the bounds of a merged partition to the lists of range bounds
2766 : */
2767 : static void
2768 816 : add_merged_range_bounds(int partnatts, FmgrInfo *partsupfuncs,
2769 : Oid *partcollations,
2770 : PartitionRangeBound *merged_lb,
2771 : PartitionRangeBound *merged_ub,
2772 : int merged_index,
2773 : List **merged_datums,
2774 : List **merged_kinds,
2775 : List **merged_indexes)
2776 : {
2777 : int cmpval;
2778 :
2779 816 : if (!*merged_datums)
2780 : {
2781 : /* First merged partition */
2782 : Assert(!*merged_kinds);
2783 : Assert(!*merged_indexes);
2784 330 : cmpval = 1;
2785 : }
2786 : else
2787 : {
2788 : PartitionRangeBound prev_ub;
2789 :
2790 : Assert(*merged_datums);
2791 : Assert(*merged_kinds);
2792 : Assert(*merged_indexes);
2793 :
2794 : /* Get the last upper bound. */
2795 486 : prev_ub.index = llast_int(*merged_indexes);
2796 486 : prev_ub.datums = (Datum *) llast(*merged_datums);
2797 486 : prev_ub.kind = (PartitionRangeDatumKind *) llast(*merged_kinds);
2798 486 : prev_ub.lower = false;
2799 :
2800 : /*
2801 : * We pass lower1 = false to partition_rbound_cmp() to prevent it from
2802 : * considering the last upper bound to be smaller than the lower bound
2803 : * of the merged partition when the values of the two range bounds
2804 : * compare equal.
2805 : */
2806 486 : cmpval = partition_rbound_cmp(partnatts, partsupfuncs, partcollations,
2807 : merged_lb->datums, merged_lb->kind,
2808 : false, &prev_ub);
2809 : Assert(cmpval >= 0);
2810 : }
2811 :
2812 : /*
2813 : * If the lower bound is higher than the last upper bound, add the lower
2814 : * bound with the index as -1 indicating that that is a lower bound; else,
2815 : * the last upper bound will be reused as the lower bound of the merged
2816 : * partition, so skip this.
2817 : */
2818 816 : if (cmpval > 0)
2819 : {
2820 576 : *merged_datums = lappend(*merged_datums, merged_lb->datums);
2821 576 : *merged_kinds = lappend(*merged_kinds, merged_lb->kind);
2822 576 : *merged_indexes = lappend_int(*merged_indexes, -1);
2823 : }
2824 :
2825 : /* Add the upper bound and index of the merged partition. */
2826 816 : *merged_datums = lappend(*merged_datums, merged_ub->datums);
2827 816 : *merged_kinds = lappend(*merged_kinds, merged_ub->kind);
2828 816 : *merged_indexes = lappend_int(*merged_indexes, merged_index);
2829 816 : }
2830 :
2831 : /*
2832 : * partitions_are_ordered
2833 : * Determine whether the partitions described by 'boundinfo' are ordered,
2834 : * that is partitions appearing earlier in the PartitionDesc sequence
2835 : * contain partition keys strictly less than those appearing later.
2836 : * Also, if NULL values are possible, they must come in the last
2837 : * partition defined in the PartitionDesc. 'live_parts' marks which
2838 : * partitions we should include when checking the ordering. Partitions
2839 : * that do not appear in 'live_parts' are ignored.
2840 : *
2841 : * If out of order, or there is insufficient info to know the order,
2842 : * then we return false.
2843 : */
2844 : bool
2845 29166 : partitions_are_ordered(PartitionBoundInfo boundinfo, Bitmapset *live_parts)
2846 : {
2847 : Assert(boundinfo != NULL);
2848 :
2849 29166 : switch (boundinfo->strategy)
2850 : {
2851 17802 : case PARTITION_STRATEGY_RANGE:
2852 :
2853 : /*
2854 : * RANGE-type partitioning guarantees that the partitions can be
2855 : * scanned in the order that they're defined in the PartitionDesc
2856 : * to provide sequential, non-overlapping ranges of tuples.
2857 : * However, if a DEFAULT partition exists and it's contained
2858 : * within live_parts, then the partitions are not ordered.
2859 : */
2860 17802 : if (!partition_bound_has_default(boundinfo) ||
2861 2100 : !bms_is_member(boundinfo->default_index, live_parts))
2862 16330 : return true;
2863 1472 : break;
2864 :
2865 10414 : case PARTITION_STRATEGY_LIST:
2866 :
2867 : /*
2868 : * LIST partitioned are ordered providing none of live_parts
2869 : * overlap with the partitioned table's interleaved partitions.
2870 : */
2871 10414 : if (!bms_overlap(live_parts, boundinfo->interleaved_parts))
2872 7960 : return true;
2873 :
2874 2454 : break;
2875 950 : case PARTITION_STRATEGY_HASH:
2876 950 : break;
2877 : }
2878 :
2879 4876 : return false;
2880 : }
2881 :
2882 : /*
2883 : * check_new_partition_bound
2884 : *
2885 : * Checks if the new partition's bound overlaps any of the existing partitions
2886 : * of parent. Also performs additional checks as necessary per strategy.
2887 : */
2888 : void
2889 11376 : check_new_partition_bound(char *relname, Relation parent,
2890 : PartitionBoundSpec *spec, ParseState *pstate)
2891 : {
2892 11376 : PartitionKey key = RelationGetPartitionKey(parent);
2893 11376 : PartitionDesc partdesc = RelationGetPartitionDesc(parent, false);
2894 11376 : PartitionBoundInfo boundinfo = partdesc->boundinfo;
2895 11376 : int with = -1;
2896 11376 : bool overlap = false;
2897 11376 : int overlap_location = -1;
2898 :
2899 11376 : if (spec->is_default)
2900 : {
2901 : /*
2902 : * The default partition bound never conflicts with any other
2903 : * partition's; if that's what we're attaching, the only possible
2904 : * problem is that one already exists, so check for that and we're
2905 : * done.
2906 : */
2907 714 : if (boundinfo == NULL || !partition_bound_has_default(boundinfo))
2908 690 : return;
2909 :
2910 : /* Default partition already exists, error out. */
2911 24 : ereport(ERROR,
2912 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2913 : errmsg("partition \"%s\" conflicts with existing default partition \"%s\"",
2914 : relname, get_rel_name(partdesc->oids[boundinfo->default_index])),
2915 : parser_errposition(pstate, spec->location)));
2916 : }
2917 :
2918 10662 : switch (key->strategy)
2919 : {
2920 708 : case PARTITION_STRATEGY_HASH:
2921 : {
2922 : Assert(spec->strategy == PARTITION_STRATEGY_HASH);
2923 : Assert(spec->remainder >= 0 && spec->remainder < spec->modulus);
2924 :
2925 708 : if (partdesc->nparts > 0)
2926 : {
2927 : int greatest_modulus;
2928 : int remainder;
2929 : int offset;
2930 :
2931 : /*
2932 : * Check rule that every modulus must be a factor of the
2933 : * next larger modulus. (For example, if you have a bunch
2934 : * of partitions that all have modulus 5, you can add a
2935 : * new partition with modulus 10 or a new partition with
2936 : * modulus 15, but you cannot add both a partition with
2937 : * modulus 10 and a partition with modulus 15, because 10
2938 : * is not a factor of 15.) We need only check the next
2939 : * smaller and next larger existing moduli, relying on
2940 : * previous enforcement of this rule to be sure that the
2941 : * rest are in line.
2942 : */
2943 :
2944 : /*
2945 : * Get the greatest (modulus, remainder) pair contained in
2946 : * boundinfo->datums that is less than or equal to the
2947 : * (spec->modulus, spec->remainder) pair.
2948 : */
2949 448 : offset = partition_hash_bsearch(boundinfo,
2950 : spec->modulus,
2951 : spec->remainder);
2952 448 : if (offset < 0)
2953 : {
2954 : int next_modulus;
2955 :
2956 : /*
2957 : * All existing moduli are greater or equal, so the
2958 : * new one must be a factor of the smallest one, which
2959 : * is first in the boundinfo.
2960 : */
2961 14 : next_modulus = DatumGetInt32(boundinfo->datums[0][0]);
2962 14 : if (next_modulus % spec->modulus != 0)
2963 6 : ereport(ERROR,
2964 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2965 : errmsg("every hash partition modulus must be a factor of the next larger modulus"),
2966 : errdetail("The new modulus %d is not a factor of %d, the modulus of existing partition \"%s\".",
2967 : spec->modulus, next_modulus,
2968 : get_rel_name(partdesc->oids[0]))));
2969 : }
2970 : else
2971 : {
2972 : int prev_modulus;
2973 :
2974 : /*
2975 : * We found the largest (modulus, remainder) pair less
2976 : * than or equal to the new one. That modulus must be
2977 : * a divisor of, or equal to, the new modulus.
2978 : */
2979 434 : prev_modulus = DatumGetInt32(boundinfo->datums[offset][0]);
2980 :
2981 434 : if (spec->modulus % prev_modulus != 0)
2982 6 : ereport(ERROR,
2983 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2984 : errmsg("every hash partition modulus must be a factor of the next larger modulus"),
2985 : errdetail("The new modulus %d is not divisible by %d, the modulus of existing partition \"%s\".",
2986 : spec->modulus,
2987 : prev_modulus,
2988 : get_rel_name(partdesc->oids[offset]))));
2989 :
2990 428 : if (offset + 1 < boundinfo->ndatums)
2991 : {
2992 : int next_modulus;
2993 :
2994 : /*
2995 : * Look at the next higher (modulus, remainder)
2996 : * pair. That could have the same modulus and a
2997 : * larger remainder than the new pair, in which
2998 : * case we're good. If it has a larger modulus,
2999 : * the new modulus must divide that one.
3000 : */
3001 30 : next_modulus = DatumGetInt32(boundinfo->datums[offset + 1][0]);
3002 :
3003 30 : if (next_modulus % spec->modulus != 0)
3004 6 : ereport(ERROR,
3005 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3006 : errmsg("every hash partition modulus must be a factor of the next larger modulus"),
3007 : errdetail("The new modulus %d is not a factor of %d, the modulus of existing partition \"%s\".",
3008 : spec->modulus, next_modulus,
3009 : get_rel_name(partdesc->oids[offset + 1]))));
3010 : }
3011 : }
3012 :
3013 430 : greatest_modulus = boundinfo->nindexes;
3014 430 : remainder = spec->remainder;
3015 :
3016 : /*
3017 : * Normally, the lowest remainder that could conflict with
3018 : * the new partition is equal to the remainder specified
3019 : * for the new partition, but when the new partition has a
3020 : * modulus higher than any used so far, we need to adjust.
3021 : */
3022 430 : if (remainder >= greatest_modulus)
3023 12 : remainder = remainder % greatest_modulus;
3024 :
3025 : /* Check every potentially-conflicting remainder. */
3026 : do
3027 : {
3028 556 : if (boundinfo->indexes[remainder] != -1)
3029 : {
3030 24 : overlap = true;
3031 24 : overlap_location = spec->location;
3032 24 : with = boundinfo->indexes[remainder];
3033 24 : break;
3034 : }
3035 532 : remainder += spec->modulus;
3036 532 : } while (remainder < greatest_modulus);
3037 : }
3038 :
3039 690 : break;
3040 : }
3041 :
3042 4916 : case PARTITION_STRATEGY_LIST:
3043 : {
3044 : Assert(spec->strategy == PARTITION_STRATEGY_LIST);
3045 :
3046 4916 : if (partdesc->nparts > 0)
3047 : {
3048 : ListCell *cell;
3049 :
3050 : Assert(boundinfo &&
3051 : boundinfo->strategy == PARTITION_STRATEGY_LIST &&
3052 : (boundinfo->ndatums > 0 ||
3053 : partition_bound_accepts_nulls(boundinfo) ||
3054 : partition_bound_has_default(boundinfo)));
3055 :
3056 6596 : foreach(cell, spec->listdatums)
3057 : {
3058 4090 : Const *val = lfirst_node(Const, cell);
3059 :
3060 4090 : overlap_location = val->location;
3061 4090 : if (!val->constisnull)
3062 : {
3063 : int offset;
3064 : bool equal;
3065 :
3066 3944 : offset = partition_list_bsearch(&key->partsupfunc[0],
3067 : key->partcollation,
3068 : boundinfo,
3069 : val->constvalue,
3070 : &equal);
3071 3944 : if (offset >= 0 && equal)
3072 : {
3073 18 : overlap = true;
3074 18 : with = boundinfo->indexes[offset];
3075 18 : break;
3076 : }
3077 : }
3078 146 : else if (partition_bound_accepts_nulls(boundinfo))
3079 : {
3080 6 : overlap = true;
3081 6 : with = boundinfo->null_index;
3082 6 : break;
3083 : }
3084 : }
3085 : }
3086 :
3087 4916 : break;
3088 : }
3089 :
3090 5038 : case PARTITION_STRATEGY_RANGE:
3091 : {
3092 : PartitionRangeBound *lower,
3093 : *upper;
3094 : int cmpval;
3095 :
3096 : Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
3097 5038 : lower = make_one_partition_rbound(key, -1, spec->lowerdatums, true);
3098 5038 : upper = make_one_partition_rbound(key, -1, spec->upperdatums, false);
3099 :
3100 : /*
3101 : * First check if the resulting range would be empty with
3102 : * specified lower and upper bounds. partition_rbound_cmp
3103 : * cannot return zero here, since the lower-bound flags are
3104 : * different.
3105 : */
3106 5038 : cmpval = partition_rbound_cmp(key->partnatts,
3107 : key->partsupfunc,
3108 : key->partcollation,
3109 : lower->datums, lower->kind,
3110 : true, upper);
3111 : Assert(cmpval != 0);
3112 5038 : if (cmpval > 0)
3113 : {
3114 : /* Point to problematic key in the lower datums list. */
3115 12 : PartitionRangeDatum *datum = list_nth(spec->lowerdatums,
3116 : cmpval - 1);
3117 :
3118 12 : ereport(ERROR,
3119 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3120 : errmsg("empty range bound specified for partition \"%s\"",
3121 : relname),
3122 : errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
3123 : get_range_partbound_string(spec->lowerdatums),
3124 : get_range_partbound_string(spec->upperdatums)),
3125 : parser_errposition(pstate, datum->location)));
3126 : }
3127 :
3128 5026 : if (partdesc->nparts > 0)
3129 : {
3130 : int offset;
3131 :
3132 : Assert(boundinfo &&
3133 : boundinfo->strategy == PARTITION_STRATEGY_RANGE &&
3134 : (boundinfo->ndatums > 0 ||
3135 : partition_bound_has_default(boundinfo)));
3136 :
3137 : /*
3138 : * Test whether the new lower bound (which is treated
3139 : * inclusively as part of the new partition) lies inside
3140 : * an existing partition, or in a gap.
3141 : *
3142 : * If it's inside an existing partition, the bound at
3143 : * offset + 1 will be the upper bound of that partition,
3144 : * and its index will be >= 0.
3145 : *
3146 : * If it's in a gap, the bound at offset + 1 will be the
3147 : * lower bound of the next partition, and its index will
3148 : * be -1. This is also true if there is no next partition,
3149 : * since the index array is initialised with an extra -1
3150 : * at the end.
3151 : */
3152 2820 : offset = partition_range_bsearch(key->partnatts,
3153 : key->partsupfunc,
3154 : key->partcollation,
3155 : boundinfo, lower,
3156 : &cmpval);
3157 :
3158 2820 : if (boundinfo->indexes[offset + 1] < 0)
3159 : {
3160 : /*
3161 : * Check that the new partition will fit in the gap.
3162 : * For it to fit, the new upper bound must be less
3163 : * than or equal to the lower bound of the next
3164 : * partition, if there is one.
3165 : */
3166 2784 : if (offset + 1 < boundinfo->ndatums)
3167 : {
3168 : Datum *datums;
3169 : PartitionRangeDatumKind *kind;
3170 : bool is_lower;
3171 :
3172 90 : datums = boundinfo->datums[offset + 1];
3173 90 : kind = boundinfo->kind[offset + 1];
3174 90 : is_lower = (boundinfo->indexes[offset + 1] == -1);
3175 :
3176 90 : cmpval = partition_rbound_cmp(key->partnatts,
3177 : key->partsupfunc,
3178 : key->partcollation,
3179 : datums, kind,
3180 : is_lower, upper);
3181 90 : if (cmpval < 0)
3182 : {
3183 : /*
3184 : * Point to problematic key in the upper
3185 : * datums list.
3186 : */
3187 : PartitionRangeDatum *datum =
3188 12 : list_nth(spec->upperdatums, abs(cmpval) - 1);
3189 :
3190 : /*
3191 : * The new partition overlaps with the
3192 : * existing partition between offset + 1 and
3193 : * offset + 2.
3194 : */
3195 12 : overlap = true;
3196 12 : overlap_location = datum->location;
3197 12 : with = boundinfo->indexes[offset + 2];
3198 : }
3199 : }
3200 : }
3201 : else
3202 : {
3203 : /*
3204 : * The new partition overlaps with the existing
3205 : * partition between offset and offset + 1.
3206 : */
3207 : PartitionRangeDatum *datum;
3208 :
3209 : /*
3210 : * Point to problematic key in the lower datums list;
3211 : * if we have equality, point to the first one.
3212 : */
3213 36 : datum = cmpval == 0 ? linitial(spec->lowerdatums) :
3214 18 : list_nth(spec->lowerdatums, abs(cmpval) - 1);
3215 36 : overlap = true;
3216 36 : overlap_location = datum->location;
3217 36 : with = boundinfo->indexes[offset + 1];
3218 : }
3219 : }
3220 :
3221 5026 : break;
3222 : }
3223 : }
3224 :
3225 10632 : if (overlap)
3226 : {
3227 : Assert(with >= 0);
3228 96 : ereport(ERROR,
3229 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3230 : errmsg("partition \"%s\" would overlap partition \"%s\"",
3231 : relname, get_rel_name(partdesc->oids[with])),
3232 : parser_errposition(pstate, overlap_location)));
3233 : }
3234 : }
3235 :
3236 : /*
3237 : * check_default_partition_contents
3238 : *
3239 : * This function checks if there exists a row in the default partition that
3240 : * would properly belong to the new partition being added. If it finds one,
3241 : * it throws an error.
3242 : */
3243 : void
3244 348 : check_default_partition_contents(Relation parent, Relation default_rel,
3245 : PartitionBoundSpec *new_spec)
3246 : {
3247 : List *new_part_constraints;
3248 : List *def_part_constraints;
3249 : List *all_parts;
3250 : ListCell *lc;
3251 :
3252 696 : new_part_constraints = (new_spec->strategy == PARTITION_STRATEGY_LIST)
3253 162 : ? get_qual_for_list(parent, new_spec)
3254 348 : : get_qual_for_range(parent, new_spec, false);
3255 : def_part_constraints =
3256 348 : get_proposed_default_constraint(new_part_constraints);
3257 :
3258 : /*
3259 : * Map the Vars in the constraint expression from parent's attnos to
3260 : * default_rel's.
3261 : */
3262 : def_part_constraints =
3263 348 : map_partition_varattnos(def_part_constraints, 1, default_rel,
3264 : parent);
3265 :
3266 : /*
3267 : * If the existing constraints on the default partition imply that it will
3268 : * not contain any row that would belong to the new partition, we can
3269 : * avoid scanning the default partition.
3270 : */
3271 348 : if (PartConstraintImpliedByRelConstraint(default_rel, def_part_constraints))
3272 : {
3273 14 : ereport(DEBUG1,
3274 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
3275 : RelationGetRelationName(default_rel))));
3276 14 : return;
3277 : }
3278 :
3279 : /*
3280 : * Scan the default partition and its subpartitions, and check for rows
3281 : * that do not satisfy the revised partition constraints.
3282 : */
3283 334 : if (default_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
3284 52 : all_parts = find_all_inheritors(RelationGetRelid(default_rel),
3285 : AccessExclusiveLock, NULL);
3286 : else
3287 282 : all_parts = list_make1_oid(RelationGetRelid(default_rel));
3288 :
3289 792 : foreach(lc, all_parts)
3290 : {
3291 476 : Oid part_relid = lfirst_oid(lc);
3292 : Relation part_rel;
3293 : Expr *partition_constraint;
3294 : EState *estate;
3295 476 : ExprState *partqualstate = NULL;
3296 : Snapshot snapshot;
3297 : ExprContext *econtext;
3298 : TableScanDesc scan;
3299 : MemoryContext oldCxt;
3300 : TupleTableSlot *tupslot;
3301 :
3302 : /* Lock already taken above. */
3303 476 : if (part_relid != RelationGetRelid(default_rel))
3304 : {
3305 142 : part_rel = table_open(part_relid, NoLock);
3306 :
3307 : /*
3308 : * Map the Vars in the constraint expression from default_rel's
3309 : * the sub-partition's.
3310 : */
3311 142 : partition_constraint = make_ands_explicit(def_part_constraints);
3312 : partition_constraint = (Expr *)
3313 142 : map_partition_varattnos((List *) partition_constraint, 1,
3314 : part_rel, default_rel);
3315 :
3316 : /*
3317 : * If the partition constraints on default partition child imply
3318 : * that it will not contain any row that would belong to the new
3319 : * partition, we can avoid scanning the child table.
3320 : */
3321 142 : if (PartConstraintImpliedByRelConstraint(part_rel,
3322 : def_part_constraints))
3323 : {
3324 8 : ereport(DEBUG1,
3325 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
3326 : RelationGetRelationName(part_rel))));
3327 :
3328 8 : table_close(part_rel, NoLock);
3329 8 : continue;
3330 : }
3331 : }
3332 : else
3333 : {
3334 334 : part_rel = default_rel;
3335 334 : partition_constraint = make_ands_explicit(def_part_constraints);
3336 : }
3337 :
3338 : /*
3339 : * Only RELKIND_RELATION relations (i.e. leaf partitions) need to be
3340 : * scanned.
3341 : */
3342 468 : if (part_rel->rd_rel->relkind != RELKIND_RELATION)
3343 : {
3344 52 : if (part_rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
3345 0 : ereport(WARNING,
3346 : (errcode(ERRCODE_CHECK_VIOLATION),
3347 : errmsg("skipped scanning foreign table \"%s\" which is a partition of default partition \"%s\"",
3348 : RelationGetRelationName(part_rel),
3349 : RelationGetRelationName(default_rel))));
3350 :
3351 52 : if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
3352 0 : table_close(part_rel, NoLock);
3353 :
3354 52 : continue;
3355 : }
3356 :
3357 416 : estate = CreateExecutorState();
3358 :
3359 : /* Build expression execution states for partition check quals */
3360 416 : partqualstate = ExecPrepareExpr(partition_constraint, estate);
3361 :
3362 416 : econtext = GetPerTupleExprContext(estate);
3363 416 : snapshot = RegisterSnapshot(GetLatestSnapshot());
3364 416 : tupslot = table_slot_create(part_rel, &estate->es_tupleTable);
3365 416 : scan = table_beginscan(part_rel, snapshot, 0, NULL);
3366 :
3367 : /*
3368 : * Switch to per-tuple memory context and reset it for each tuple
3369 : * produced, so we don't leak memory.
3370 : */
3371 416 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
3372 :
3373 880 : while (table_scan_getnextslot(scan, ForwardScanDirection, tupslot))
3374 : {
3375 66 : econtext->ecxt_scantuple = tupslot;
3376 :
3377 66 : if (!ExecCheck(partqualstate, econtext))
3378 18 : ereport(ERROR,
3379 : (errcode(ERRCODE_CHECK_VIOLATION),
3380 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
3381 : RelationGetRelationName(default_rel)),
3382 : errtable(default_rel)));
3383 :
3384 48 : ResetExprContext(econtext);
3385 48 : CHECK_FOR_INTERRUPTS();
3386 : }
3387 :
3388 398 : MemoryContextSwitchTo(oldCxt);
3389 398 : table_endscan(scan);
3390 398 : UnregisterSnapshot(snapshot);
3391 398 : ExecDropSingleTupleTableSlot(tupslot);
3392 398 : FreeExecutorState(estate);
3393 :
3394 398 : if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
3395 134 : table_close(part_rel, NoLock); /* keep the lock until commit */
3396 : }
3397 : }
3398 :
3399 : /*
3400 : * get_hash_partition_greatest_modulus
3401 : *
3402 : * Returns the greatest modulus of the hash partition bound.
3403 : * This is no longer used in the core code, but we keep it around
3404 : * in case external modules are using it.
3405 : */
3406 : int
3407 0 : get_hash_partition_greatest_modulus(PartitionBoundInfo bound)
3408 : {
3409 : Assert(bound && bound->strategy == PARTITION_STRATEGY_HASH);
3410 0 : return bound->nindexes;
3411 : }
3412 :
3413 : /*
3414 : * make_one_partition_rbound
3415 : *
3416 : * Return a PartitionRangeBound given a list of PartitionRangeDatum elements
3417 : * and a flag telling whether the bound is lower or not. Made into a function
3418 : * because there are multiple sites that want to use this facility.
3419 : */
3420 : static PartitionRangeBound *
3421 51394 : make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
3422 : {
3423 : PartitionRangeBound *bound;
3424 : ListCell *lc;
3425 : int i;
3426 :
3427 : Assert(datums != NIL);
3428 :
3429 51394 : bound = palloc0_object(PartitionRangeBound);
3430 51394 : bound->index = index;
3431 51394 : bound->datums = palloc0_array(Datum, key->partnatts);
3432 51394 : bound->kind = palloc0_array(PartitionRangeDatumKind, key->partnatts);
3433 51394 : bound->lower = lower;
3434 :
3435 51394 : i = 0;
3436 113784 : foreach(lc, datums)
3437 : {
3438 62390 : PartitionRangeDatum *datum = lfirst_node(PartitionRangeDatum, lc);
3439 :
3440 : /* What's contained in this range datum? */
3441 62390 : bound->kind[i] = datum->kind;
3442 :
3443 62390 : if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
3444 : {
3445 58778 : Const *val = castNode(Const, datum->value);
3446 :
3447 58778 : if (val->constisnull)
3448 0 : elog(ERROR, "invalid range bound datum");
3449 58778 : bound->datums[i] = val->constvalue;
3450 : }
3451 :
3452 62390 : i++;
3453 : }
3454 :
3455 51394 : return bound;
3456 : }
3457 :
3458 : /*
3459 : * partition_rbound_cmp
3460 : *
3461 : * For two range bounds this decides whether the 1st one (specified by
3462 : * datums1, kind1, and lower1) is <, =, or > the bound specified in *b2.
3463 : *
3464 : * 0 is returned if they are equal, otherwise a non-zero integer whose sign
3465 : * indicates the ordering, and whose absolute value gives the 1-based
3466 : * partition key number of the first mismatching column.
3467 : *
3468 : * partnatts, partsupfunc and partcollation give the number of attributes in the
3469 : * bounds to be compared, comparison function to be used and the collations of
3470 : * attributes, respectively.
3471 : *
3472 : * Note that if the values of the two range bounds compare equal, then we take
3473 : * into account whether they are upper or lower bounds, and an upper bound is
3474 : * considered to be smaller than a lower bound. This is important to the way
3475 : * that RelationBuildPartitionDesc() builds the PartitionBoundInfoData
3476 : * structure, which only stores the upper bound of a common boundary between
3477 : * two contiguous partitions.
3478 : */
3479 : static int32
3480 49146 : partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
3481 : Oid *partcollation,
3482 : Datum *datums1, PartitionRangeDatumKind *kind1,
3483 : bool lower1, PartitionRangeBound *b2)
3484 : {
3485 49146 : int32 colnum = 0;
3486 49146 : int32 cmpval = 0; /* placate compiler */
3487 : int i;
3488 49146 : Datum *datums2 = b2->datums;
3489 49146 : PartitionRangeDatumKind *kind2 = b2->kind;
3490 49146 : bool lower2 = b2->lower;
3491 :
3492 68580 : for (i = 0; i < partnatts; i++)
3493 : {
3494 : /* Track column number in case we need it for result */
3495 55800 : colnum++;
3496 :
3497 : /*
3498 : * First, handle cases where the column is unbounded, which should not
3499 : * invoke the comparison procedure, and should not consider any later
3500 : * columns. Note that the PartitionRangeDatumKind enum elements
3501 : * compare the same way as the values they represent.
3502 : */
3503 55800 : if (kind1[i] < kind2[i])
3504 2082 : return -colnum;
3505 53718 : else if (kind1[i] > kind2[i])
3506 126 : return colnum;
3507 53592 : else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
3508 : {
3509 : /*
3510 : * The column bounds are both MINVALUE or both MAXVALUE. No later
3511 : * columns should be considered, but we still need to compare
3512 : * whether they are upper or lower bounds.
3513 : */
3514 258 : break;
3515 : }
3516 :
3517 53334 : cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
3518 53334 : partcollation[i],
3519 53334 : datums1[i],
3520 53334 : datums2[i]));
3521 53334 : if (cmpval != 0)
3522 33900 : break;
3523 : }
3524 :
3525 : /*
3526 : * If the comparison is anything other than equal, we're done. If they
3527 : * compare equal though, we still have to consider whether the boundaries
3528 : * are inclusive or exclusive. Exclusive one is considered smaller of the
3529 : * two.
3530 : */
3531 46938 : if (cmpval == 0 && lower1 != lower2)
3532 11034 : cmpval = lower1 ? 1 : -1;
3533 :
3534 46938 : return cmpval == 0 ? 0 : (cmpval < 0 ? -colnum : colnum);
3535 : }
3536 :
3537 : /*
3538 : * partition_rbound_datum_cmp
3539 : *
3540 : * Return whether range bound (specified in rb_datums and rb_kind)
3541 : * is <, =, or > partition key of tuple (tuple_datums)
3542 : *
3543 : * n_tuple_datums, partsupfunc and partcollation give number of attributes in
3544 : * the bounds to be compared, comparison function to be used and the collations
3545 : * of attributes resp.
3546 : */
3547 : int32
3548 1644092 : partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation,
3549 : const Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
3550 : const Datum *tuple_datums, int n_tuple_datums)
3551 : {
3552 : int i;
3553 1644092 : int32 cmpval = -1;
3554 :
3555 1718368 : for (i = 0; i < n_tuple_datums; i++)
3556 : {
3557 1649936 : if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
3558 68378 : return -1;
3559 1581558 : else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
3560 68686 : return 1;
3561 :
3562 1512872 : cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
3563 1512872 : partcollation[i],
3564 1512872 : rb_datums[i],
3565 1512872 : tuple_datums[i]));
3566 1512872 : if (cmpval != 0)
3567 1438596 : break;
3568 : }
3569 :
3570 1507028 : return cmpval;
3571 : }
3572 :
3573 : /*
3574 : * partition_hbound_cmp
3575 : *
3576 : * Compares modulus first, then remainder if modulus is equal.
3577 : */
3578 : static int32
3579 1696 : partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
3580 : {
3581 1696 : if (modulus1 < modulus2)
3582 174 : return -1;
3583 1522 : if (modulus1 > modulus2)
3584 60 : return 1;
3585 1462 : if (modulus1 == modulus2 && remainder1 != remainder2)
3586 1462 : return (remainder1 > remainder2) ? 1 : -1;
3587 0 : return 0;
3588 : }
3589 :
3590 : /*
3591 : * partition_list_bsearch
3592 : * Returns the index of the greatest bound datum that is less than equal
3593 : * to the given value or -1 if all of the bound datums are greater
3594 : *
3595 : * *is_equal is set to true if the bound datum at the returned index is equal
3596 : * to the input value.
3597 : */
3598 : int
3599 158666 : partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation,
3600 : PartitionBoundInfo boundinfo,
3601 : Datum value, bool *is_equal)
3602 : {
3603 : int lo,
3604 : hi,
3605 : mid;
3606 :
3607 158666 : lo = -1;
3608 158666 : hi = boundinfo->ndatums - 1;
3609 322062 : while (lo < hi)
3610 : {
3611 : int32 cmpval;
3612 :
3613 313798 : mid = (lo + hi + 1) / 2;
3614 313798 : cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
3615 : partcollation[0],
3616 313798 : boundinfo->datums[mid][0],
3617 : value));
3618 313798 : if (cmpval <= 0)
3619 : {
3620 266518 : lo = mid;
3621 266518 : *is_equal = (cmpval == 0);
3622 266518 : if (*is_equal)
3623 150402 : break;
3624 : }
3625 : else
3626 47280 : hi = mid - 1;
3627 : }
3628 :
3629 158666 : return lo;
3630 : }
3631 :
3632 : /*
3633 : * partition_range_bsearch
3634 : * Returns the index of the greatest range bound that is less than or
3635 : * equal to the given range bound or -1 if all of the range bounds are
3636 : * greater
3637 : *
3638 : * Upon return from this function, *cmpval is set to 0 if the bound at the
3639 : * returned index matches the input range bound exactly, otherwise a
3640 : * non-zero integer whose sign indicates the ordering, and whose absolute
3641 : * value gives the 1-based partition key number of the first mismatching
3642 : * column.
3643 : */
3644 : static int
3645 2820 : partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
3646 : Oid *partcollation,
3647 : PartitionBoundInfo boundinfo,
3648 : PartitionRangeBound *probe, int32 *cmpval)
3649 : {
3650 : int lo,
3651 : hi,
3652 : mid;
3653 :
3654 2820 : lo = -1;
3655 2820 : hi = boundinfo->ndatums - 1;
3656 8558 : while (lo < hi)
3657 : {
3658 5756 : mid = (lo + hi + 1) / 2;
3659 11512 : *cmpval = partition_rbound_cmp(partnatts, partsupfunc,
3660 : partcollation,
3661 5756 : boundinfo->datums[mid],
3662 5756 : boundinfo->kind[mid],
3663 5756 : (boundinfo->indexes[mid] == -1),
3664 : probe);
3665 5756 : if (*cmpval <= 0)
3666 : {
3667 5618 : lo = mid;
3668 5618 : if (*cmpval == 0)
3669 18 : break;
3670 : }
3671 : else
3672 138 : hi = mid - 1;
3673 : }
3674 :
3675 2820 : return lo;
3676 : }
3677 :
3678 : /*
3679 : * partition_range_datum_bsearch
3680 : * Returns the index of the greatest range bound that is less than or
3681 : * equal to the given tuple or -1 if all of the range bounds are greater
3682 : *
3683 : * *is_equal is set to true if the range bound at the returned index is equal
3684 : * to the input tuple.
3685 : */
3686 : int
3687 525200 : partition_range_datum_bsearch(FmgrInfo *partsupfunc, Oid *partcollation,
3688 : PartitionBoundInfo boundinfo,
3689 : int nvalues, const Datum *values, bool *is_equal)
3690 : {
3691 : int lo,
3692 : hi,
3693 : mid;
3694 :
3695 525200 : lo = -1;
3696 525200 : hi = boundinfo->ndatums - 1;
3697 1612698 : while (lo < hi)
3698 : {
3699 : int32 cmpval;
3700 :
3701 1149248 : mid = (lo + hi + 1) / 2;
3702 1149248 : cmpval = partition_rbound_datum_cmp(partsupfunc,
3703 : partcollation,
3704 1149248 : boundinfo->datums[mid],
3705 1149248 : boundinfo->kind[mid],
3706 : values,
3707 : nvalues);
3708 1149248 : if (cmpval <= 0)
3709 : {
3710 662210 : lo = mid;
3711 662210 : *is_equal = (cmpval == 0);
3712 :
3713 662210 : if (*is_equal)
3714 61750 : break;
3715 : }
3716 : else
3717 487038 : hi = mid - 1;
3718 : }
3719 :
3720 525200 : return lo;
3721 : }
3722 :
3723 : /*
3724 : * partition_hash_bsearch
3725 : * Returns the index of the greatest (modulus, remainder) pair that is
3726 : * less than or equal to the given (modulus, remainder) pair or -1 if
3727 : * all of them are greater
3728 : */
3729 : int
3730 448 : partition_hash_bsearch(PartitionBoundInfo boundinfo,
3731 : int modulus, int remainder)
3732 : {
3733 : int lo,
3734 : hi,
3735 : mid;
3736 :
3737 448 : lo = -1;
3738 448 : hi = boundinfo->ndatums - 1;
3739 1156 : while (lo < hi)
3740 : {
3741 : int32 cmpval,
3742 : bound_modulus,
3743 : bound_remainder;
3744 :
3745 708 : mid = (lo + hi + 1) / 2;
3746 708 : bound_modulus = DatumGetInt32(boundinfo->datums[mid][0]);
3747 708 : bound_remainder = DatumGetInt32(boundinfo->datums[mid][1]);
3748 708 : cmpval = partition_hbound_cmp(bound_modulus, bound_remainder,
3749 : modulus, remainder);
3750 708 : if (cmpval <= 0)
3751 : {
3752 646 : lo = mid;
3753 :
3754 646 : if (cmpval == 0)
3755 0 : break;
3756 : }
3757 : else
3758 62 : hi = mid - 1;
3759 : }
3760 :
3761 448 : return lo;
3762 : }
3763 :
3764 : /*
3765 : * qsort_partition_hbound_cmp
3766 : *
3767 : * Hash bounds are sorted by modulus, then by remainder.
3768 : */
3769 : static int32
3770 988 : qsort_partition_hbound_cmp(const void *a, const void *b)
3771 : {
3772 988 : const PartitionHashBound *h1 = (const PartitionHashBound *) a;
3773 988 : const PartitionHashBound *h2 = (const PartitionHashBound *) b;
3774 :
3775 1976 : return partition_hbound_cmp(h1->modulus, h1->remainder,
3776 988 : h2->modulus, h2->remainder);
3777 : }
3778 :
3779 : /*
3780 : * qsort_partition_list_value_cmp
3781 : *
3782 : * Compare two list partition bound datums.
3783 : */
3784 : static int32
3785 30972 : qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
3786 : {
3787 30972 : Datum val1 = ((const PartitionListValue *) a)->value,
3788 30972 : val2 = ((const PartitionListValue *) b)->value;
3789 30972 : PartitionKey key = (PartitionKey) arg;
3790 :
3791 30972 : return DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
3792 30972 : key->partcollation[0],
3793 : val1, val2));
3794 : }
3795 :
3796 : /*
3797 : * qsort_partition_rbound_cmp
3798 : *
3799 : * Used when sorting range bounds across all range partitions.
3800 : */
3801 : static int32
3802 32730 : qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
3803 : {
3804 32730 : PartitionRangeBound *b1 = (*(PartitionRangeBound *const *) a);
3805 32730 : PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
3806 32730 : PartitionKey key = (PartitionKey) arg;
3807 :
3808 32730 : return compare_range_bounds(key->partnatts, key->partsupfunc,
3809 : key->partcollation,
3810 : b1, b2);
3811 : }
3812 :
3813 : /*
3814 : * get_partition_operator
3815 : *
3816 : * Return oid of the operator of the given strategy for the given partition
3817 : * key column. It is assumed that the partitioning key is of the same type as
3818 : * the chosen partitioning opclass, or at least binary-compatible. In the
3819 : * latter case, *need_relabel is set to true if the opclass is not of a
3820 : * polymorphic type (indicating a RelabelType node needed on top), otherwise
3821 : * false.
3822 : */
3823 : static Oid
3824 17052 : get_partition_operator(PartitionKey key, int col, StrategyNumber strategy,
3825 : bool *need_relabel)
3826 : {
3827 : Oid operoid;
3828 :
3829 : /*
3830 : * Get the operator in the partitioning opfamily using the opclass'
3831 : * declared input type as both left- and righttype.
3832 : */
3833 17052 : operoid = get_opfamily_member(key->partopfamily[col],
3834 17052 : key->partopcintype[col],
3835 17052 : key->partopcintype[col],
3836 : strategy);
3837 17052 : if (!OidIsValid(operoid))
3838 0 : elog(ERROR, "missing operator %d(%u,%u) in partition opfamily %u",
3839 : strategy, key->partopcintype[col], key->partopcintype[col],
3840 : key->partopfamily[col]);
3841 :
3842 : /*
3843 : * If the partition key column is not of the same type as the operator
3844 : * class and not polymorphic, tell caller to wrap the non-Const expression
3845 : * in a RelabelType. This matches what parse_coerce.c does.
3846 : */
3847 34220 : *need_relabel = (key->parttypid[col] != key->partopcintype[col] &&
3848 17162 : key->partopcintype[col] != RECORDOID &&
3849 110 : !IsPolymorphicType(key->partopcintype[col]));
3850 :
3851 17052 : return operoid;
3852 : }
3853 :
3854 : /*
3855 : * make_partition_op_expr
3856 : * Returns an Expr for the given partition key column with arg1 and
3857 : * arg2 as its leftop and rightop, respectively
3858 : */
3859 : static Expr *
3860 17052 : make_partition_op_expr(PartitionKey key, int keynum,
3861 : uint16 strategy, Expr *arg1, Expr *arg2)
3862 : {
3863 : Oid operoid;
3864 17052 : bool need_relabel = false;
3865 17052 : Expr *result = NULL;
3866 :
3867 : /* Get the correct btree operator for this partitioning column */
3868 17052 : operoid = get_partition_operator(key, keynum, strategy, &need_relabel);
3869 :
3870 : /*
3871 : * Chosen operator may be such that the non-Const operand needs to be
3872 : * coerced, so apply the same; see the comment in
3873 : * get_partition_operator().
3874 : */
3875 17052 : if (!IsA(arg1, Const) &&
3876 12650 : (need_relabel ||
3877 12586 : key->partcollation[keynum] != key->parttypcoll[keynum]))
3878 64 : arg1 = (Expr *) makeRelabelType(arg1,
3879 64 : key->partopcintype[keynum],
3880 : -1,
3881 64 : key->partcollation[keynum],
3882 : COERCE_EXPLICIT_CAST);
3883 :
3884 : /* Generate the actual expression */
3885 17052 : switch (key->strategy)
3886 : {
3887 2754 : case PARTITION_STRATEGY_LIST:
3888 : {
3889 2754 : List *elems = (List *) arg2;
3890 2754 : int nelems = list_length(elems);
3891 :
3892 : Assert(nelems >= 1);
3893 : Assert(keynum == 0);
3894 :
3895 3760 : if (nelems > 1 &&
3896 1006 : !type_is_array(key->parttypid[keynum]))
3897 1000 : {
3898 : ArrayExpr *arrexpr;
3899 : ScalarArrayOpExpr *saopexpr;
3900 :
3901 : /* Construct an ArrayExpr for the right-hand inputs */
3902 1000 : arrexpr = makeNode(ArrayExpr);
3903 1000 : arrexpr->array_typeid =
3904 1000 : get_array_type(key->parttypid[keynum]);
3905 1000 : arrexpr->array_collid = key->parttypcoll[keynum];
3906 1000 : arrexpr->element_typeid = key->parttypid[keynum];
3907 1000 : arrexpr->elements = elems;
3908 1000 : arrexpr->multidims = false;
3909 1000 : arrexpr->location = -1;
3910 :
3911 : /* Build leftop = ANY (rightop) */
3912 1000 : saopexpr = makeNode(ScalarArrayOpExpr);
3913 1000 : saopexpr->opno = operoid;
3914 1000 : saopexpr->opfuncid = get_opcode(operoid);
3915 1000 : saopexpr->hashfuncid = InvalidOid;
3916 1000 : saopexpr->negfuncid = InvalidOid;
3917 1000 : saopexpr->useOr = true;
3918 1000 : saopexpr->inputcollid = key->partcollation[keynum];
3919 1000 : saopexpr->args = list_make2(arg1, arrexpr);
3920 1000 : saopexpr->location = -1;
3921 :
3922 1000 : result = (Expr *) saopexpr;
3923 : }
3924 : else
3925 : {
3926 1754 : List *elemops = NIL;
3927 : ListCell *lc;
3928 :
3929 3514 : foreach(lc, elems)
3930 : {
3931 1760 : Expr *elem = lfirst(lc),
3932 : *elemop;
3933 :
3934 1760 : elemop = make_opclause(operoid,
3935 : BOOLOID,
3936 : false,
3937 : arg1, elem,
3938 : InvalidOid,
3939 1760 : key->partcollation[keynum]);
3940 1760 : elemops = lappend(elemops, elemop);
3941 : }
3942 :
3943 1754 : result = nelems > 1 ? makeBoolExpr(OR_EXPR, elemops, -1) : linitial(elemops);
3944 : }
3945 2754 : break;
3946 : }
3947 :
3948 14298 : case PARTITION_STRATEGY_RANGE:
3949 14298 : result = make_opclause(operoid,
3950 : BOOLOID,
3951 : false,
3952 : arg1, arg2,
3953 : InvalidOid,
3954 14298 : key->partcollation[keynum]);
3955 14298 : break;
3956 :
3957 0 : case PARTITION_STRATEGY_HASH:
3958 : Assert(false);
3959 0 : break;
3960 : }
3961 :
3962 17052 : return result;
3963 : }
3964 :
3965 : /*
3966 : * get_qual_for_hash
3967 : *
3968 : * Returns a CHECK constraint expression to use as a hash partition's
3969 : * constraint, given the parent relation and partition bound structure.
3970 : *
3971 : * The partition constraint for a hash partition is always a call to the
3972 : * built-in function satisfies_hash_partition().
3973 : */
3974 : static List *
3975 158 : get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
3976 : {
3977 158 : PartitionKey key = RelationGetPartitionKey(parent);
3978 : FuncExpr *fexpr;
3979 : Node *relidConst;
3980 : Node *modulusConst;
3981 : Node *remainderConst;
3982 : List *args;
3983 : ListCell *partexprs_item;
3984 : int i;
3985 :
3986 : /* Fixed arguments. */
3987 158 : relidConst = (Node *) makeConst(OIDOID,
3988 : -1,
3989 : InvalidOid,
3990 : sizeof(Oid),
3991 : ObjectIdGetDatum(RelationGetRelid(parent)),
3992 : false,
3993 : true);
3994 :
3995 158 : modulusConst = (Node *) makeConst(INT4OID,
3996 : -1,
3997 : InvalidOid,
3998 : sizeof(int32),
3999 : Int32GetDatum(spec->modulus),
4000 : false,
4001 : true);
4002 :
4003 158 : remainderConst = (Node *) makeConst(INT4OID,
4004 : -1,
4005 : InvalidOid,
4006 : sizeof(int32),
4007 : Int32GetDatum(spec->remainder),
4008 : false,
4009 : true);
4010 :
4011 158 : args = list_make3(relidConst, modulusConst, remainderConst);
4012 158 : partexprs_item = list_head(key->partexprs);
4013 :
4014 : /* Add an argument for each key column. */
4015 340 : for (i = 0; i < key->partnatts; i++)
4016 : {
4017 : Node *keyCol;
4018 :
4019 : /* Left operand */
4020 182 : if (key->partattrs[i] != 0)
4021 : {
4022 176 : keyCol = (Node *) makeVar(1,
4023 176 : key->partattrs[i],
4024 176 : key->parttypid[i],
4025 176 : key->parttypmod[i],
4026 176 : key->parttypcoll[i],
4027 : 0);
4028 : }
4029 : else
4030 : {
4031 6 : keyCol = (Node *) copyObject(lfirst(partexprs_item));
4032 6 : partexprs_item = lnext(key->partexprs, partexprs_item);
4033 : }
4034 :
4035 182 : args = lappend(args, keyCol);
4036 : }
4037 :
4038 158 : fexpr = makeFuncExpr(F_SATISFIES_HASH_PARTITION,
4039 : BOOLOID,
4040 : args,
4041 : InvalidOid,
4042 : InvalidOid,
4043 : COERCE_EXPLICIT_CALL);
4044 :
4045 158 : return list_make1(fexpr);
4046 : }
4047 :
4048 : /*
4049 : * get_qual_for_list
4050 : *
4051 : * Returns an implicit-AND list of expressions to use as a list partition's
4052 : * constraint, given the parent relation and partition bound structure.
4053 : *
4054 : * The function returns NIL for a default partition when it's the only
4055 : * partition since in that case there is no constraint.
4056 : */
4057 : static List *
4058 2828 : get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
4059 : {
4060 2828 : PartitionKey key = RelationGetPartitionKey(parent);
4061 : List *result;
4062 : Expr *keyCol;
4063 : Expr *opexpr;
4064 : NullTest *nulltest;
4065 : ListCell *cell;
4066 2828 : List *elems = NIL;
4067 2828 : bool list_has_null = false;
4068 :
4069 : /*
4070 : * Only single-column list partitioning is supported, so we are worried
4071 : * only about the partition key with index 0.
4072 : */
4073 : Assert(key->partnatts == 1);
4074 :
4075 : /* Construct Var or expression representing the partition column */
4076 2828 : if (key->partattrs[0] != 0)
4077 2714 : keyCol = (Expr *) makeVar(1,
4078 2714 : key->partattrs[0],
4079 2714 : key->parttypid[0],
4080 2714 : key->parttypmod[0],
4081 2714 : key->parttypcoll[0],
4082 : 0);
4083 : else
4084 114 : keyCol = (Expr *) copyObject(linitial(key->partexprs));
4085 :
4086 : /*
4087 : * For default list partition, collect datums for all the partitions. The
4088 : * default partition constraint should check that the partition key is
4089 : * equal to none of those.
4090 : */
4091 2828 : if (spec->is_default)
4092 : {
4093 : int i;
4094 278 : int ndatums = 0;
4095 278 : PartitionDesc pdesc = RelationGetPartitionDesc(parent, false);
4096 278 : PartitionBoundInfo boundinfo = pdesc->boundinfo;
4097 :
4098 278 : if (boundinfo)
4099 : {
4100 278 : ndatums = boundinfo->ndatums;
4101 :
4102 278 : if (partition_bound_accepts_nulls(boundinfo))
4103 48 : list_has_null = true;
4104 : }
4105 :
4106 : /*
4107 : * If default is the only partition, there need not be any partition
4108 : * constraint on it.
4109 : */
4110 278 : if (ndatums == 0 && !list_has_null)
4111 38 : return NIL;
4112 :
4113 1284 : for (i = 0; i < ndatums; i++)
4114 : {
4115 : Const *val;
4116 :
4117 : /*
4118 : * Construct Const from known-not-null datum. We must be careful
4119 : * to copy the value, because our result has to be able to outlive
4120 : * the relcache entry we're copying from.
4121 : */
4122 2088 : val = makeConst(key->parttypid[0],
4123 1044 : key->parttypmod[0],
4124 1044 : key->parttypcoll[0],
4125 1044 : key->parttyplen[0],
4126 1044 : datumCopy(*boundinfo->datums[i],
4127 1044 : key->parttypbyval[0],
4128 1044 : key->parttyplen[0]),
4129 : false, /* isnull */
4130 1044 : key->parttypbyval[0]);
4131 :
4132 1044 : elems = lappend(elems, val);
4133 : }
4134 : }
4135 : else
4136 : {
4137 : /*
4138 : * Create list of Consts for the allowed values, excluding any nulls.
4139 : */
4140 6724 : foreach(cell, spec->listdatums)
4141 : {
4142 4174 : Const *val = lfirst_node(Const, cell);
4143 :
4144 4174 : if (val->constisnull)
4145 92 : list_has_null = true;
4146 : else
4147 4082 : elems = lappend(elems, copyObject(val));
4148 : }
4149 : }
4150 :
4151 2790 : if (elems)
4152 : {
4153 : /*
4154 : * Generate the operator expression from the non-null partition
4155 : * values.
4156 : */
4157 2754 : opexpr = make_partition_op_expr(key, 0, BTEqualStrategyNumber,
4158 : keyCol, (Expr *) elems);
4159 : }
4160 : else
4161 : {
4162 : /*
4163 : * If there are no partition values, we don't need an operator
4164 : * expression.
4165 : */
4166 36 : opexpr = NULL;
4167 : }
4168 :
4169 2790 : if (!list_has_null)
4170 : {
4171 : /*
4172 : * Gin up a "col IS NOT NULL" test that will be ANDed with the main
4173 : * expression. This might seem redundant, but the partition routing
4174 : * machinery needs it.
4175 : */
4176 2650 : nulltest = makeNode(NullTest);
4177 2650 : nulltest->arg = keyCol;
4178 2650 : nulltest->nulltesttype = IS_NOT_NULL;
4179 2650 : nulltest->argisrow = false;
4180 2650 : nulltest->location = -1;
4181 :
4182 2650 : result = opexpr ? list_make2(nulltest, opexpr) : list_make1(nulltest);
4183 : }
4184 : else
4185 : {
4186 : /*
4187 : * Gin up a "col IS NULL" test that will be OR'd with the main
4188 : * expression.
4189 : */
4190 140 : nulltest = makeNode(NullTest);
4191 140 : nulltest->arg = keyCol;
4192 140 : nulltest->nulltesttype = IS_NULL;
4193 140 : nulltest->argisrow = false;
4194 140 : nulltest->location = -1;
4195 :
4196 140 : if (opexpr)
4197 : {
4198 : Expr *or;
4199 :
4200 104 : or = makeBoolExpr(OR_EXPR, list_make2(nulltest, opexpr), -1);
4201 104 : result = list_make1(or);
4202 : }
4203 : else
4204 36 : result = list_make1(nulltest);
4205 : }
4206 :
4207 : /*
4208 : * Note that, in general, applying NOT to a constraint expression doesn't
4209 : * necessarily invert the set of rows it accepts, because NOT (NULL) is
4210 : * NULL. However, the partition constraints we construct here never
4211 : * evaluate to NULL, so applying NOT works as intended.
4212 : */
4213 2790 : if (spec->is_default)
4214 : {
4215 240 : result = list_make1(make_ands_explicit(result));
4216 240 : result = list_make1(makeBoolExpr(NOT_EXPR, result, -1));
4217 : }
4218 :
4219 2790 : return result;
4220 : }
4221 :
4222 : /*
4223 : * get_qual_for_range
4224 : *
4225 : * Returns an implicit-AND list of expressions to use as a range partition's
4226 : * constraint, given the parent relation and partition bound structure.
4227 : *
4228 : * For a multi-column range partition key, say (a, b, c), with (al, bl, cl)
4229 : * as the lower bound tuple and (au, bu, cu) as the upper bound tuple, we
4230 : * generate an expression tree of the following form:
4231 : *
4232 : * (a IS NOT NULL) and (b IS NOT NULL) and (c IS NOT NULL)
4233 : * AND
4234 : * (a > al OR (a = al AND b > bl) OR (a = al AND b = bl AND c >= cl))
4235 : * AND
4236 : * (a < au OR (a = au AND b < bu) OR (a = au AND b = bu AND c < cu))
4237 : *
4238 : * It is often the case that a prefix of lower and upper bound tuples contains
4239 : * the same values, for example, (al = au), in which case, we will emit an
4240 : * expression tree of the following form:
4241 : *
4242 : * (a IS NOT NULL) and (b IS NOT NULL) and (c IS NOT NULL)
4243 : * AND
4244 : * (a = al)
4245 : * AND
4246 : * (b > bl OR (b = bl AND c >= cl))
4247 : * AND
4248 : * (b < bu OR (b = bu AND c < cu))
4249 : *
4250 : * If a bound datum is either MINVALUE or MAXVALUE, these expressions are
4251 : * simplified using the fact that any value is greater than MINVALUE and less
4252 : * than MAXVALUE. So, for example, if cu = MAXVALUE, c < cu is automatically
4253 : * true, and we need not emit any expression for it, and the last line becomes
4254 : *
4255 : * (b < bu) OR (b = bu), which is simplified to (b <= bu)
4256 : *
4257 : * In most common cases with only one partition column, say a, the following
4258 : * expression tree will be generated: a IS NOT NULL AND a >= al AND a < au
4259 : *
4260 : * For default partition, it returns the negation of the constraints of all
4261 : * the other partitions.
4262 : *
4263 : * External callers should pass for_default as false; we set it to true only
4264 : * when recursing.
4265 : */
4266 : static List *
4267 4588 : get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
4268 : bool for_default)
4269 : {
4270 4588 : List *result = NIL;
4271 : ListCell *cell1,
4272 : *cell2,
4273 : *partexprs_item,
4274 : *partexprs_item_saved;
4275 : int i,
4276 : j;
4277 : PartitionRangeDatum *ldatum,
4278 : *udatum;
4279 4588 : PartitionKey key = RelationGetPartitionKey(parent);
4280 : Expr *keyCol;
4281 : Const *lower_val,
4282 : *upper_val;
4283 : List *lower_or_arms,
4284 : *upper_or_arms;
4285 : int num_or_arms,
4286 : current_or_arm;
4287 : ListCell *lower_or_start_datum,
4288 : *upper_or_start_datum;
4289 : bool need_next_lower_arm,
4290 : need_next_upper_arm;
4291 :
4292 4588 : if (spec->is_default)
4293 : {
4294 346 : List *or_expr_args = NIL;
4295 346 : PartitionDesc pdesc = RelationGetPartitionDesc(parent, false);
4296 346 : Oid *inhoids = pdesc->oids;
4297 346 : int nparts = pdesc->nparts,
4298 : k;
4299 :
4300 1394 : for (k = 0; k < nparts; k++)
4301 : {
4302 1048 : Oid inhrelid = inhoids[k];
4303 : HeapTuple tuple;
4304 : Datum datum;
4305 : PartitionBoundSpec *bspec;
4306 :
4307 1048 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(inhrelid));
4308 1048 : if (!HeapTupleIsValid(tuple))
4309 0 : elog(ERROR, "cache lookup failed for relation %u", inhrelid);
4310 :
4311 1048 : datum = SysCacheGetAttrNotNull(RELOID, tuple,
4312 : Anum_pg_class_relpartbound);
4313 : bspec = (PartitionBoundSpec *)
4314 1048 : stringToNode(TextDatumGetCString(datum));
4315 1048 : if (!IsA(bspec, PartitionBoundSpec))
4316 0 : elog(ERROR, "expected PartitionBoundSpec");
4317 :
4318 1048 : if (!bspec->is_default)
4319 : {
4320 : List *part_qual;
4321 :
4322 702 : part_qual = get_qual_for_range(parent, bspec, true);
4323 :
4324 : /*
4325 : * AND the constraints of the partition and add to
4326 : * or_expr_args
4327 : */
4328 1404 : or_expr_args = lappend(or_expr_args, list_length(part_qual) > 1
4329 684 : ? makeBoolExpr(AND_EXPR, part_qual, -1)
4330 18 : : linitial(part_qual));
4331 : }
4332 1048 : ReleaseSysCache(tuple);
4333 : }
4334 :
4335 346 : if (or_expr_args != NIL)
4336 : {
4337 : Expr *other_parts_constr;
4338 :
4339 : /*
4340 : * Combine the constraints obtained for non-default partitions
4341 : * using OR. As requested, each of the OR's args doesn't include
4342 : * the NOT NULL test for partition keys (which is to avoid its
4343 : * useless repetition). Add the same now.
4344 : */
4345 : other_parts_constr =
4346 532 : makeBoolExpr(AND_EXPR,
4347 : lappend(get_range_nulltest(key),
4348 266 : list_length(or_expr_args) > 1
4349 202 : ? makeBoolExpr(OR_EXPR, or_expr_args,
4350 : -1)
4351 64 : : linitial(or_expr_args)),
4352 : -1);
4353 :
4354 : /*
4355 : * Finally, the default partition contains everything *NOT*
4356 : * contained in the non-default partitions.
4357 : */
4358 266 : result = list_make1(makeBoolExpr(NOT_EXPR,
4359 : list_make1(other_parts_constr), -1));
4360 : }
4361 :
4362 346 : return result;
4363 : }
4364 :
4365 : /*
4366 : * If it is the recursive call for default, we skip the get_range_nulltest
4367 : * to avoid accumulating the NullTest on the same keys for each partition.
4368 : */
4369 4242 : if (!for_default)
4370 3540 : result = get_range_nulltest(key);
4371 :
4372 : /*
4373 : * Iterate over the key columns and check if the corresponding lower and
4374 : * upper datums are equal using the btree equality operator for the
4375 : * column's type. If equal, we emit single keyCol = common_value
4376 : * expression. Starting from the first column for which the corresponding
4377 : * lower and upper bound datums are not equal, we generate OR expressions
4378 : * as shown in the function's header comment.
4379 : */
4380 4242 : i = 0;
4381 4242 : partexprs_item = list_head(key->partexprs);
4382 4242 : partexprs_item_saved = partexprs_item; /* placate compiler */
4383 4794 : forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
4384 : {
4385 : EState *estate;
4386 : MemoryContext oldcxt;
4387 : Expr *test_expr;
4388 : ExprState *test_exprstate;
4389 : Datum test_result;
4390 : bool isNull;
4391 :
4392 4794 : ldatum = lfirst_node(PartitionRangeDatum, cell1);
4393 4794 : udatum = lfirst_node(PartitionRangeDatum, cell2);
4394 :
4395 : /*
4396 : * Since get_range_key_properties() modifies partexprs_item, and we
4397 : * might need to start over from the previous expression in the later
4398 : * part of this function, save away the current value.
4399 : */
4400 4794 : partexprs_item_saved = partexprs_item;
4401 :
4402 4794 : get_range_key_properties(key, i, ldatum, udatum,
4403 : &partexprs_item,
4404 : &keyCol,
4405 : &lower_val, &upper_val);
4406 :
4407 : /*
4408 : * If either value is NULL, the corresponding partition bound is
4409 : * either MINVALUE or MAXVALUE, and we treat them as unequal, because
4410 : * even if they're the same, there is no common value to equate the
4411 : * key column with.
4412 : */
4413 4794 : if (!lower_val || !upper_val)
4414 : break;
4415 :
4416 : /* Create the test expression */
4417 4402 : estate = CreateExecutorState();
4418 4402 : oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
4419 4402 : test_expr = make_partition_op_expr(key, i, BTEqualStrategyNumber,
4420 : (Expr *) lower_val,
4421 : (Expr *) upper_val);
4422 4402 : fix_opfuncids((Node *) test_expr);
4423 4402 : test_exprstate = ExecInitExpr(test_expr, NULL);
4424 4402 : test_result = ExecEvalExprSwitchContext(test_exprstate,
4425 4402 : GetPerTupleExprContext(estate),
4426 : &isNull);
4427 4402 : MemoryContextSwitchTo(oldcxt);
4428 4402 : FreeExecutorState(estate);
4429 :
4430 : /* If not equal, go generate the OR expressions */
4431 4402 : if (!DatumGetBool(test_result))
4432 3850 : break;
4433 :
4434 : /*
4435 : * The bounds for the last key column can't be equal, because such a
4436 : * range partition would never be allowed to be defined (it would have
4437 : * an empty range otherwise).
4438 : */
4439 552 : if (i == key->partnatts - 1)
4440 0 : elog(ERROR, "invalid range bound specification");
4441 :
4442 : /* Equal, so generate keyCol = lower_val expression */
4443 552 : result = lappend(result,
4444 552 : make_partition_op_expr(key, i, BTEqualStrategyNumber,
4445 : keyCol, (Expr *) lower_val));
4446 :
4447 552 : i++;
4448 : }
4449 :
4450 : /* First pair of lower_val and upper_val that are not equal. */
4451 4242 : lower_or_start_datum = cell1;
4452 4242 : upper_or_start_datum = cell2;
4453 :
4454 : /* OR will have as many arms as there are key columns left. */
4455 4242 : num_or_arms = key->partnatts - i;
4456 4242 : current_or_arm = 0;
4457 4242 : lower_or_arms = upper_or_arms = NIL;
4458 4242 : need_next_lower_arm = need_next_upper_arm = true;
4459 4588 : while (current_or_arm < num_or_arms)
4460 : {
4461 4588 : List *lower_or_arm_args = NIL,
4462 4588 : *upper_or_arm_args = NIL;
4463 :
4464 : /* Restart scan of columns from the i'th one */
4465 4588 : j = i;
4466 4588 : partexprs_item = partexprs_item_saved;
4467 :
4468 5018 : for_both_cell(cell1, spec->lowerdatums, lower_or_start_datum,
4469 : cell2, spec->upperdatums, upper_or_start_datum)
4470 : {
4471 5018 : PartitionRangeDatum *ldatum_next = NULL,
4472 5018 : *udatum_next = NULL;
4473 :
4474 5018 : ldatum = lfirst_node(PartitionRangeDatum, cell1);
4475 5018 : if (lnext(spec->lowerdatums, cell1))
4476 854 : ldatum_next = castNode(PartitionRangeDatum,
4477 : lfirst(lnext(spec->lowerdatums, cell1)));
4478 5018 : udatum = lfirst_node(PartitionRangeDatum, cell2);
4479 5018 : if (lnext(spec->upperdatums, cell2))
4480 854 : udatum_next = castNode(PartitionRangeDatum,
4481 : lfirst(lnext(spec->upperdatums, cell2)));
4482 5018 : get_range_key_properties(key, j, ldatum, udatum,
4483 : &partexprs_item,
4484 : &keyCol,
4485 : &lower_val, &upper_val);
4486 :
4487 5018 : if (need_next_lower_arm && lower_val)
4488 : {
4489 : uint16 strategy;
4490 :
4491 : /*
4492 : * For the non-last columns of this arm, use the EQ operator.
4493 : * For the last column of this arm, use GT, unless this is the
4494 : * last column of the whole bound check, or the next bound
4495 : * datum is MINVALUE, in which case use GE.
4496 : */
4497 4716 : if (j - i < current_or_arm)
4498 376 : strategy = BTEqualStrategyNumber;
4499 4340 : else if (j == key->partnatts - 1 ||
4500 364 : (ldatum_next &&
4501 364 : ldatum_next->kind == PARTITION_RANGE_DATUM_MINVALUE))
4502 4018 : strategy = BTGreaterEqualStrategyNumber;
4503 : else
4504 322 : strategy = BTGreaterStrategyNumber;
4505 :
4506 4716 : lower_or_arm_args = lappend(lower_or_arm_args,
4507 4716 : make_partition_op_expr(key, j,
4508 : strategy,
4509 : keyCol,
4510 : (Expr *) lower_val));
4511 : }
4512 :
4513 5018 : if (need_next_upper_arm && upper_val)
4514 : {
4515 : uint16 strategy;
4516 :
4517 : /*
4518 : * For the non-last columns of this arm, use the EQ operator.
4519 : * For the last column of this arm, use LT, unless the next
4520 : * bound datum is MAXVALUE, in which case use LE.
4521 : */
4522 4628 : if (j - i < current_or_arm)
4523 310 : strategy = BTEqualStrategyNumber;
4524 4318 : else if (udatum_next &&
4525 328 : udatum_next->kind == PARTITION_RANGE_DATUM_MAXVALUE)
4526 30 : strategy = BTLessEqualStrategyNumber;
4527 : else
4528 4288 : strategy = BTLessStrategyNumber;
4529 :
4530 4628 : upper_or_arm_args = lappend(upper_or_arm_args,
4531 4628 : make_partition_op_expr(key, j,
4532 : strategy,
4533 : keyCol,
4534 : (Expr *) upper_val));
4535 : }
4536 :
4537 : /*
4538 : * Did we generate enough of OR's arguments? First arm considers
4539 : * the first of the remaining columns, second arm considers first
4540 : * two of the remaining columns, and so on.
4541 : */
4542 5018 : ++j;
4543 5018 : if (j - i > current_or_arm)
4544 : {
4545 : /*
4546 : * We must not emit any more arms if the new column that will
4547 : * be considered is unbounded, or this one was.
4548 : */
4549 4588 : if (!lower_val || !ldatum_next ||
4550 364 : ldatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
4551 4278 : need_next_lower_arm = false;
4552 4588 : if (!upper_val || !udatum_next ||
4553 328 : udatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
4554 4326 : need_next_upper_arm = false;
4555 4588 : break;
4556 : }
4557 : }
4558 :
4559 4588 : if (lower_or_arm_args != NIL)
4560 8680 : lower_or_arms = lappend(lower_or_arms,
4561 4340 : list_length(lower_or_arm_args) > 1
4562 310 : ? makeBoolExpr(AND_EXPR, lower_or_arm_args, -1)
4563 4030 : : linitial(lower_or_arm_args));
4564 :
4565 4588 : if (upper_or_arm_args != NIL)
4566 8636 : upper_or_arms = lappend(upper_or_arms,
4567 4318 : list_length(upper_or_arm_args) > 1
4568 262 : ? makeBoolExpr(AND_EXPR, upper_or_arm_args, -1)
4569 4056 : : linitial(upper_or_arm_args));
4570 :
4571 : /* If no work to do in the next iteration, break away. */
4572 4588 : if (!need_next_lower_arm && !need_next_upper_arm)
4573 4242 : break;
4574 :
4575 346 : ++current_or_arm;
4576 : }
4577 :
4578 : /*
4579 : * Generate the OR expressions for each of lower and upper bounds (if
4580 : * required), and append to the list of implicitly ANDed list of
4581 : * expressions.
4582 : */
4583 4242 : if (lower_or_arms != NIL)
4584 8060 : result = lappend(result,
4585 4030 : list_length(lower_or_arms) > 1
4586 244 : ? makeBoolExpr(OR_EXPR, lower_or_arms, -1)
4587 3786 : : linitial(lower_or_arms));
4588 4242 : if (upper_or_arms != NIL)
4589 8112 : result = lappend(result,
4590 4056 : list_length(upper_or_arms) > 1
4591 214 : ? makeBoolExpr(OR_EXPR, upper_or_arms, -1)
4592 3842 : : linitial(upper_or_arms));
4593 :
4594 : /*
4595 : * As noted above, for non-default, we return list with constant TRUE. If
4596 : * the result is NIL during the recursive call for default, it implies
4597 : * this is the only other partition which can hold every value of the key
4598 : * except NULL. Hence we return the NullTest result skipped earlier.
4599 : */
4600 4242 : if (result == NIL)
4601 0 : result = for_default
4602 0 : ? get_range_nulltest(key)
4603 0 : : list_make1(makeBoolConst(true, false));
4604 :
4605 4242 : return result;
4606 : }
4607 :
4608 : /*
4609 : * get_range_key_properties
4610 : * Returns range partition key information for a given column
4611 : *
4612 : * This is a subroutine for get_qual_for_range, and its API is pretty
4613 : * specialized to that caller.
4614 : *
4615 : * Constructs an Expr for the key column (returned in *keyCol) and Consts
4616 : * for the lower and upper range limits (returned in *lower_val and
4617 : * *upper_val). For MINVALUE/MAXVALUE limits, NULL is returned instead of
4618 : * a Const. All of these structures are freshly palloc'd.
4619 : *
4620 : * *partexprs_item points to the cell containing the next expression in
4621 : * the key->partexprs list, or NULL. It may be advanced upon return.
4622 : */
4623 : static void
4624 9812 : get_range_key_properties(PartitionKey key, int keynum,
4625 : PartitionRangeDatum *ldatum,
4626 : PartitionRangeDatum *udatum,
4627 : ListCell **partexprs_item,
4628 : Expr **keyCol,
4629 : Const **lower_val, Const **upper_val)
4630 : {
4631 : /* Get partition key expression for this column */
4632 9812 : if (key->partattrs[keynum] != 0)
4633 : {
4634 8940 : *keyCol = (Expr *) makeVar(1,
4635 8940 : key->partattrs[keynum],
4636 8940 : key->parttypid[keynum],
4637 8940 : key->parttypmod[keynum],
4638 8940 : key->parttypcoll[keynum],
4639 : 0);
4640 : }
4641 : else
4642 : {
4643 872 : if (*partexprs_item == NULL)
4644 0 : elog(ERROR, "wrong number of partition key expressions");
4645 872 : *keyCol = copyObject(lfirst(*partexprs_item));
4646 872 : *partexprs_item = lnext(key->partexprs, *partexprs_item);
4647 : }
4648 :
4649 : /* Get appropriate Const nodes for the bounds */
4650 9812 : if (ldatum->kind == PARTITION_RANGE_DATUM_VALUE)
4651 9316 : *lower_val = castNode(Const, copyObject(ldatum->value));
4652 : else
4653 496 : *lower_val = NULL;
4654 :
4655 9812 : if (udatum->kind == PARTITION_RANGE_DATUM_VALUE)
4656 9260 : *upper_val = castNode(Const, copyObject(udatum->value));
4657 : else
4658 552 : *upper_val = NULL;
4659 9812 : }
4660 :
4661 : /*
4662 : * get_range_nulltest
4663 : *
4664 : * A non-default range partition table does not currently allow partition
4665 : * keys to be null, so emit an IS NOT NULL expression for each key column.
4666 : */
4667 : static List *
4668 3806 : get_range_nulltest(PartitionKey key)
4669 : {
4670 3806 : List *result = NIL;
4671 : NullTest *nulltest;
4672 : ListCell *partexprs_item;
4673 : int i;
4674 :
4675 3806 : partexprs_item = list_head(key->partexprs);
4676 8450 : for (i = 0; i < key->partnatts; i++)
4677 : {
4678 : Expr *keyCol;
4679 :
4680 4644 : if (key->partattrs[i] != 0)
4681 : {
4682 4220 : keyCol = (Expr *) makeVar(1,
4683 4220 : key->partattrs[i],
4684 4220 : key->parttypid[i],
4685 4220 : key->parttypmod[i],
4686 4220 : key->parttypcoll[i],
4687 : 0);
4688 : }
4689 : else
4690 : {
4691 424 : if (partexprs_item == NULL)
4692 0 : elog(ERROR, "wrong number of partition key expressions");
4693 424 : keyCol = copyObject(lfirst(partexprs_item));
4694 424 : partexprs_item = lnext(key->partexprs, partexprs_item);
4695 : }
4696 :
4697 4644 : nulltest = makeNode(NullTest);
4698 4644 : nulltest->arg = keyCol;
4699 4644 : nulltest->nulltesttype = IS_NOT_NULL;
4700 4644 : nulltest->argisrow = false;
4701 4644 : nulltest->location = -1;
4702 4644 : result = lappend(result, nulltest);
4703 : }
4704 :
4705 3806 : return result;
4706 : }
4707 :
4708 : /*
4709 : * compute_partition_hash_value
4710 : *
4711 : * Compute the hash value for given partition key values.
4712 : */
4713 : uint64
4714 212654 : compute_partition_hash_value(int partnatts, FmgrInfo *partsupfunc, const Oid *partcollation,
4715 : const Datum *values, const bool *isnull)
4716 : {
4717 : int i;
4718 212654 : uint64 rowHash = 0;
4719 212654 : Datum seed = UInt64GetDatum(HASH_PARTITION_SEED);
4720 :
4721 426280 : for (i = 0; i < partnatts; i++)
4722 : {
4723 : /* Nulls are just ignored */
4724 213638 : if (!isnull[i])
4725 : {
4726 : Datum hash;
4727 :
4728 : Assert(OidIsValid(partsupfunc[i].fn_oid));
4729 :
4730 : /*
4731 : * Compute hash for each datum value by calling respective
4732 : * datatype-specific hash functions of each partition key
4733 : * attribute.
4734 : */
4735 212996 : hash = FunctionCall2Coll(&partsupfunc[i], partcollation[i],
4736 212996 : values[i], seed);
4737 :
4738 : /* Form a single 64-bit hash value */
4739 212984 : rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
4740 : }
4741 : }
4742 :
4743 212642 : return rowHash;
4744 : }
4745 :
4746 : /*
4747 : * satisfies_hash_partition
4748 : *
4749 : * This is an SQL-callable function for use in hash partition constraints.
4750 : * The first three arguments are the parent table OID, modulus, and remainder.
4751 : * The remaining arguments are the value of the partitioning columns (or
4752 : * expressions); these are hashed and the results are combined into a single
4753 : * hash value by calling hash_combine64.
4754 : *
4755 : * Returns true if remainder produced when this computed single hash value is
4756 : * divided by the given modulus is equal to given remainder, otherwise false.
4757 : * NB: it's important that this never return null, as the constraint machinery
4758 : * would consider that to be a "pass".
4759 : *
4760 : * See get_qual_for_hash() for usage.
4761 : */
4762 : Datum
4763 4228 : satisfies_hash_partition(PG_FUNCTION_ARGS)
4764 : {
4765 : typedef struct ColumnsHashData
4766 : {
4767 : Oid relid;
4768 : int nkeys;
4769 : Oid variadic_type;
4770 : int16 variadic_typlen;
4771 : bool variadic_typbyval;
4772 : char variadic_typalign;
4773 : Oid partcollid[PARTITION_MAX_KEYS];
4774 : FmgrInfo partsupfunc[FLEXIBLE_ARRAY_MEMBER];
4775 : } ColumnsHashData;
4776 : Oid parentId;
4777 : int modulus;
4778 : int remainder;
4779 4228 : Datum seed = UInt64GetDatum(HASH_PARTITION_SEED);
4780 : ColumnsHashData *my_extra;
4781 4228 : uint64 rowHash = 0;
4782 :
4783 : /* Return false if the parent OID, modulus, or remainder is NULL. */
4784 4228 : if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2))
4785 12 : PG_RETURN_BOOL(false);
4786 4216 : parentId = PG_GETARG_OID(0);
4787 4216 : modulus = PG_GETARG_INT32(1);
4788 4216 : remainder = PG_GETARG_INT32(2);
4789 :
4790 : /* Sanity check modulus and remainder. */
4791 4216 : if (modulus <= 0)
4792 6 : ereport(ERROR,
4793 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4794 : errmsg("modulus for hash partition must be an integer value greater than zero")));
4795 4210 : if (remainder < 0)
4796 6 : ereport(ERROR,
4797 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4798 : errmsg("remainder for hash partition must be an integer value greater than or equal to zero")));
4799 4204 : if (remainder >= modulus)
4800 6 : ereport(ERROR,
4801 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4802 : errmsg("remainder for hash partition must be less than modulus")));
4803 :
4804 : /*
4805 : * Cache hash function information.
4806 : */
4807 4198 : my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
4808 4198 : if (my_extra == NULL || my_extra->relid != parentId)
4809 : {
4810 : Relation parent;
4811 : PartitionKey key;
4812 : int j;
4813 :
4814 : /* Open parent relation and fetch partition key info */
4815 2196 : parent = relation_open(parentId, AccessShareLock);
4816 2190 : key = RelationGetPartitionKey(parent);
4817 :
4818 : /* Reject parent table that is not hash-partitioned. */
4819 2190 : if (key == NULL || key->strategy != PARTITION_STRATEGY_HASH)
4820 12 : ereport(ERROR,
4821 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4822 : errmsg("\"%s\" is not a hash partitioned table",
4823 : get_rel_name(parentId))));
4824 :
4825 2178 : if (!get_fn_expr_variadic(fcinfo->flinfo))
4826 : {
4827 2148 : int nargs = PG_NARGS() - 3;
4828 :
4829 : /* complain if wrong number of column values */
4830 2148 : if (key->partnatts != nargs)
4831 12 : ereport(ERROR,
4832 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4833 : errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
4834 : key->partnatts, nargs)));
4835 :
4836 : /* allocate space for our cache */
4837 4272 : fcinfo->flinfo->fn_extra =
4838 2136 : MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
4839 : offsetof(ColumnsHashData, partsupfunc) +
4840 2136 : sizeof(FmgrInfo) * nargs);
4841 2136 : my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
4842 2136 : my_extra->relid = parentId;
4843 2136 : my_extra->nkeys = key->partnatts;
4844 2136 : memcpy(my_extra->partcollid, key->partcollation,
4845 2136 : key->partnatts * sizeof(Oid));
4846 :
4847 : /* check argument types and save fmgr_infos */
4848 4314 : for (j = 0; j < key->partnatts; ++j)
4849 : {
4850 2184 : Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, j + 3);
4851 :
4852 2184 : if (argtype != key->parttypid[j] && !IsBinaryCoercible(argtype, key->parttypid[j]))
4853 6 : ereport(ERROR,
4854 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4855 : errmsg("column %d of the partition key has type %s, but supplied value is of type %s",
4856 : j + 1, format_type_be(key->parttypid[j]), format_type_be(argtype))));
4857 :
4858 2178 : fmgr_info_copy(&my_extra->partsupfunc[j],
4859 2178 : &key->partsupfunc[j],
4860 2178 : fcinfo->flinfo->fn_mcxt);
4861 : }
4862 : }
4863 : else
4864 : {
4865 30 : ArrayType *variadic_array = PG_GETARG_ARRAYTYPE_P(3);
4866 :
4867 : /* allocate space for our cache -- just one FmgrInfo in this case */
4868 60 : fcinfo->flinfo->fn_extra =
4869 30 : MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
4870 : offsetof(ColumnsHashData, partsupfunc) +
4871 : sizeof(FmgrInfo));
4872 30 : my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
4873 30 : my_extra->relid = parentId;
4874 30 : my_extra->nkeys = key->partnatts;
4875 30 : my_extra->variadic_type = ARR_ELEMTYPE(variadic_array);
4876 30 : get_typlenbyvalalign(my_extra->variadic_type,
4877 : &my_extra->variadic_typlen,
4878 : &my_extra->variadic_typbyval,
4879 : &my_extra->variadic_typalign);
4880 30 : my_extra->partcollid[0] = key->partcollation[0];
4881 :
4882 : /* check argument types */
4883 72 : for (j = 0; j < key->partnatts; ++j)
4884 54 : if (key->parttypid[j] != my_extra->variadic_type)
4885 12 : ereport(ERROR,
4886 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4887 : errmsg("column %d of the partition key has type \"%s\", but supplied value is of type \"%s\"",
4888 : j + 1,
4889 : format_type_be(key->parttypid[j]),
4890 : format_type_be(my_extra->variadic_type))));
4891 :
4892 18 : fmgr_info_copy(&my_extra->partsupfunc[0],
4893 : &key->partsupfunc[0],
4894 18 : fcinfo->flinfo->fn_mcxt);
4895 : }
4896 :
4897 : /* Hold lock until commit */
4898 2148 : relation_close(parent, NoLock);
4899 : }
4900 :
4901 4150 : if (!OidIsValid(my_extra->variadic_type))
4902 : {
4903 4132 : int nkeys = my_extra->nkeys;
4904 : int i;
4905 :
4906 : /*
4907 : * For a non-variadic call, neither the number of arguments nor their
4908 : * types can change across calls, so avoid the expense of rechecking
4909 : * here.
4910 : */
4911 :
4912 8306 : for (i = 0; i < nkeys; i++)
4913 : {
4914 : Datum hash;
4915 :
4916 : /* keys start from fourth argument of function. */
4917 4174 : int argno = i + 3;
4918 :
4919 4174 : if (PG_ARGISNULL(argno))
4920 0 : continue;
4921 :
4922 4174 : hash = FunctionCall2Coll(&my_extra->partsupfunc[i],
4923 : my_extra->partcollid[i],
4924 : PG_GETARG_DATUM(argno),
4925 : seed);
4926 :
4927 : /* Form a single 64-bit hash value */
4928 4174 : rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
4929 : }
4930 : }
4931 : else
4932 : {
4933 18 : ArrayType *variadic_array = PG_GETARG_ARRAYTYPE_P(3);
4934 : int i;
4935 : int nelems;
4936 : Datum *datum;
4937 : bool *isnull;
4938 :
4939 18 : deconstruct_array(variadic_array,
4940 : my_extra->variadic_type,
4941 18 : my_extra->variadic_typlen,
4942 18 : my_extra->variadic_typbyval,
4943 18 : my_extra->variadic_typalign,
4944 : &datum, &isnull, &nelems);
4945 :
4946 : /* complain if wrong number of column values */
4947 18 : if (nelems != my_extra->nkeys)
4948 6 : ereport(ERROR,
4949 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4950 : errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
4951 : my_extra->nkeys, nelems)));
4952 :
4953 36 : for (i = 0; i < nelems; i++)
4954 : {
4955 : Datum hash;
4956 :
4957 24 : if (isnull[i])
4958 0 : continue;
4959 :
4960 24 : hash = FunctionCall2Coll(&my_extra->partsupfunc[0],
4961 : my_extra->partcollid[0],
4962 24 : datum[i],
4963 : seed);
4964 :
4965 : /* Form a single 64-bit hash value */
4966 24 : rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
4967 : }
4968 : }
4969 :
4970 4144 : PG_RETURN_BOOL(rowHash % modulus == remainder);
4971 : }
4972 :
4973 : /*
4974 : * check_two_partitions_bounds_range
4975 : *
4976 : * (function for BY RANGE partitioning)
4977 : *
4978 : * This is a helper function for check_partitions_for_split() and
4979 : * calculate_partition_bound_for_merge(). This function compares the upper
4980 : * bound of first_bound and the lower bound of second_bound. These bounds
4981 : * should be equal except when "defaultPart == true" (this means that one of
4982 : * the split partitions is DEFAULT). In this case, the upper bound of
4983 : * first_bound can be less than the lower bound of second_bound because
4984 : * the space between these bounds will be included in the DEFAULT partition.
4985 : *
4986 : * parent: partitioned table
4987 : * first_name: name of the first partition
4988 : * first_bound: bound of the first partition
4989 : * second_name: name of the second partition
4990 : * second_bound: bound of the second partition
4991 : * defaultPart: true if one of the new partitions is DEFAULT
4992 : * is_merge: true indicates the operation is MERGE PARTITIONS;
4993 : * false indicates the operation is SPLIT PARTITION.
4994 : * pstate: pointer to ParseState struct for determining error position
4995 : */
4996 : static void
4997 552 : check_two_partitions_bounds_range(Relation parent,
4998 : RangeVar *first_name,
4999 : PartitionBoundSpec *first_bound,
5000 : RangeVar *second_name,
5001 : PartitionBoundSpec *second_bound,
5002 : bool defaultPart,
5003 : bool is_merge,
5004 : ParseState *pstate)
5005 : {
5006 552 : PartitionKey key = RelationGetPartitionKey(parent);
5007 : PartitionRangeBound *first_upper;
5008 : PartitionRangeBound *second_lower;
5009 : int cmpval;
5010 :
5011 : Assert(key->strategy == PARTITION_STRATEGY_RANGE);
5012 :
5013 552 : first_upper = make_one_partition_rbound(key, -1, first_bound->upperdatums, false);
5014 552 : second_lower = make_one_partition_rbound(key, -1, second_bound->lowerdatums, true);
5015 :
5016 : /*
5017 : * lower1 argument of partition_rbound_cmp() is set to false for the
5018 : * correct comparison result of the lower and upper bounds.
5019 : */
5020 552 : cmpval = partition_rbound_cmp(key->partnatts,
5021 : key->partsupfunc,
5022 : key->partcollation,
5023 : second_lower->datums, second_lower->kind,
5024 : false, first_upper);
5025 552 : if ((!defaultPart && cmpval) || (defaultPart && cmpval < 0))
5026 : {
5027 42 : PartitionRangeDatum *datum = linitial(second_bound->lowerdatums);
5028 :
5029 42 : if (is_merge)
5030 12 : ereport(ERROR,
5031 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5032 : errmsg("can not merge partition \"%s\" together with partition \"%s\"",
5033 : second_name->relname, first_name->relname),
5034 : errdetail("lower bound of partition \"%s\" is not equal to the upper bound of partition \"%s\"",
5035 : second_name->relname, first_name->relname),
5036 : errhint("ALTER TABLE ... MERGE PARTITIONS requires the partition bounds to be adjacent."),
5037 : parser_errposition(pstate, datum->location));
5038 : else
5039 30 : ereport(ERROR,
5040 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5041 : errmsg("can not split to partition \"%s\" together with partition \"%s\"",
5042 : second_name->relname, first_name->relname),
5043 : errdetail("lower bound of partition \"%s\" is not equal to the upper bound of partition \"%s\"",
5044 : second_name->relname, first_name->relname),
5045 : errhint("ALTER TABLE ... SPLIT PARTITION requires the partition bounds to be adjacent."),
5046 : parser_errposition(pstate, datum->location));
5047 : }
5048 510 : }
5049 :
5050 : /*
5051 : * get_partition_bound_spec
5052 : *
5053 : * Returns the PartitionBoundSpec for the partition with the given OID partOid.
5054 : */
5055 : static PartitionBoundSpec *
5056 792 : get_partition_bound_spec(Oid partOid)
5057 : {
5058 : HeapTuple tuple;
5059 : Datum datum;
5060 : bool isnull;
5061 792 : PartitionBoundSpec *boundspec = NULL;
5062 :
5063 : /* Try fetching the tuple from the catcache, for speed. */
5064 792 : tuple = SearchSysCache1(RELOID, partOid);
5065 792 : if (!HeapTupleIsValid(tuple))
5066 0 : elog(ERROR, "cache lookup failed for relation %u", partOid);
5067 :
5068 792 : datum = SysCacheGetAttr(RELOID, tuple,
5069 : Anum_pg_class_relpartbound,
5070 : &isnull);
5071 792 : if (isnull)
5072 0 : elog(ERROR, "partition bound for relation %u is null",
5073 : partOid);
5074 :
5075 792 : boundspec = stringToNode(TextDatumGetCString(datum));
5076 :
5077 792 : if (!IsA(boundspec, PartitionBoundSpec))
5078 0 : elog(ERROR, "expected PartitionBoundSpec for relation %u",
5079 : partOid);
5080 :
5081 792 : ReleaseSysCache(tuple);
5082 792 : return boundspec;
5083 : }
5084 :
5085 : /*
5086 : * calculate_partition_bound_for_merge
5087 : *
5088 : * Calculates the bound of the merged partition "spec" by using the bounds of
5089 : * the partitions to be merged.
5090 : *
5091 : * parent: partitioned table
5092 : * partNames: names of partitions to be merged
5093 : * partOids: Oids of partitions to be merged
5094 : * spec (out): bounds specification of the merged partition
5095 : * pstate: pointer to ParseState struct to determine error position
5096 : */
5097 : void
5098 186 : calculate_partition_bound_for_merge(Relation parent,
5099 : List *partNames,
5100 : List *partOids,
5101 : PartitionBoundSpec *spec,
5102 : ParseState *pstate)
5103 : {
5104 186 : PartitionKey key = RelationGetPartitionKey(parent);
5105 : PartitionBoundSpec *bound;
5106 :
5107 : Assert(!spec->is_default);
5108 :
5109 186 : switch (key->strategy)
5110 : {
5111 180 : case PARTITION_STRATEGY_RANGE:
5112 : {
5113 : int i;
5114 : PartitionRangeBound **lower_bounds;
5115 180 : int nparts = list_length(partOids);
5116 180 : List *bounds = NIL;
5117 :
5118 180 : lower_bounds = palloc0_array(PartitionRangeBound *, nparts);
5119 :
5120 : /*
5121 : * Create an array of lower bounds and a list of
5122 : * PartitionBoundSpec.
5123 : */
5124 762 : foreach_oid(partoid, partOids)
5125 : {
5126 402 : bound = get_partition_bound_spec(partoid);
5127 402 : i = foreach_current_index(partoid);
5128 :
5129 402 : lower_bounds[i] = make_one_partition_rbound(key, i, bound->lowerdatums, true);
5130 402 : bounds = lappend(bounds, bound);
5131 : }
5132 :
5133 : /* Sort the array of lower bounds. */
5134 180 : qsort_arg(lower_bounds, nparts, sizeof(PartitionRangeBound *),
5135 : qsort_partition_rbound_cmp, key);
5136 :
5137 : /* Ranges of partitions should be adjacent. */
5138 384 : for (i = 1; i < nparts; i++)
5139 : {
5140 216 : int index = lower_bounds[i]->index;
5141 216 : int prev_index = lower_bounds[i - 1]->index;
5142 :
5143 216 : check_two_partitions_bounds_range(parent,
5144 216 : (RangeVar *) list_nth(partNames, prev_index),
5145 216 : (PartitionBoundSpec *) list_nth(bounds, prev_index),
5146 216 : (RangeVar *) list_nth(partNames, index),
5147 216 : (PartitionBoundSpec *) list_nth(bounds, index),
5148 : false,
5149 : true,
5150 : pstate);
5151 : }
5152 :
5153 : /*
5154 : * The lower bound of the first partition is the lower bound
5155 : * of the merged partition.
5156 : */
5157 168 : spec->lowerdatums =
5158 168 : ((PartitionBoundSpec *) list_nth(bounds, lower_bounds[0]->index))->lowerdatums;
5159 :
5160 : /*
5161 : * The upper bound of the last partition is the upper bound of
5162 : * the merged partition.
5163 : */
5164 168 : spec->upperdatums =
5165 168 : ((PartitionBoundSpec *) list_nth(bounds, lower_bounds[nparts - 1]->index))->upperdatums;
5166 :
5167 168 : pfree(lower_bounds);
5168 168 : list_free(bounds);
5169 168 : break;
5170 : }
5171 :
5172 6 : case PARTITION_STRATEGY_LIST:
5173 : {
5174 : /* Consolidate bounds for all partitions in the list. */
5175 30 : foreach_oid(partoid, partOids)
5176 : {
5177 18 : bound = get_partition_bound_spec(partoid);
5178 18 : spec->listdatums = list_concat(spec->listdatums, bound->listdatums);
5179 : }
5180 6 : break;
5181 : }
5182 :
5183 0 : default:
5184 0 : elog(ERROR, "unexpected partition strategy: %d",
5185 : (int) key->strategy);
5186 : }
5187 174 : }
5188 :
5189 : /*
5190 : * partitions_listdatum_intersection
5191 : *
5192 : * (function for BY LIST partitioning)
5193 : *
5194 : * Function compares lists of values for different partitions.
5195 : * Return a list that contains *one* cell that is present in both list1 and
5196 : * list2. The returned list is freshly allocated via palloc(), but the
5197 : * cells themselves point to the same objects as the cells of the
5198 : * input lists.
5199 : *
5200 : * Currently, there is no need to collect all common partition datums from the
5201 : * two lists.
5202 : */
5203 : static List *
5204 72 : partitions_listdatum_intersection(FmgrInfo *partsupfunc, Oid *partcollation,
5205 : const List *list1, const List *list2)
5206 : {
5207 72 : List *result = NIL;
5208 :
5209 72 : if (list1 == NIL || list2 == NIL)
5210 0 : return result;
5211 :
5212 312 : foreach_node(Const, val1, list1)
5213 : {
5214 192 : bool isnull1 = val1->constisnull;
5215 :
5216 924 : foreach_node(Const, val2, list2)
5217 : {
5218 564 : if (val2->constisnull)
5219 : {
5220 36 : if (isnull1)
5221 : {
5222 0 : result = lappend(result, val1);
5223 12 : return result;
5224 : }
5225 36 : continue;
5226 : }
5227 528 : else if (isnull1)
5228 0 : continue;
5229 :
5230 : /* Compare two datum values. */
5231 528 : if (DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
5232 : partcollation[0],
5233 : val1->constvalue,
5234 : val2->constvalue)) == 0)
5235 : {
5236 12 : result = lappend(result, val1);
5237 12 : return result;
5238 : }
5239 : }
5240 : }
5241 :
5242 60 : return result;
5243 : }
5244 :
5245 : /*
5246 : * check_partitions_not_overlap_list
5247 : *
5248 : * (function for BY LIST partitioning)
5249 : *
5250 : * This is a helper function for check_partitions_for_split().
5251 : * Checks that the values of the new partitions do not overlap.
5252 : *
5253 : * parent: partitioned table
5254 : * parts: array of SinglePartitionSpec structs with info about split partitions
5255 : * nparts: size of array "parts"
5256 : */
5257 : static void
5258 30 : check_partitions_not_overlap_list(Relation parent,
5259 : SinglePartitionSpec **parts,
5260 : int nparts,
5261 : ParseState *pstate)
5262 : {
5263 30 : PartitionKey key PG_USED_FOR_ASSERTS_ONLY = RelationGetPartitionKey(parent);
5264 : int i,
5265 : j;
5266 : SinglePartitionSpec *sps1,
5267 : *sps2;
5268 : List *overlap;
5269 :
5270 : Assert(key->strategy == PARTITION_STRATEGY_LIST);
5271 :
5272 84 : for (i = 0; i < nparts; i++)
5273 : {
5274 66 : sps1 = parts[i];
5275 :
5276 126 : for (j = i + 1; j < nparts; j++)
5277 : {
5278 72 : sps2 = parts[j];
5279 :
5280 72 : overlap = partitions_listdatum_intersection(&key->partsupfunc[0],
5281 : key->partcollation,
5282 72 : sps1->bound->listdatums,
5283 72 : sps2->bound->listdatums);
5284 72 : if (list_length(overlap) > 0)
5285 : {
5286 12 : Const *val = (Const *) linitial_node(Const, overlap);
5287 :
5288 12 : ereport(ERROR,
5289 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5290 : errmsg("new partition \"%s\" would overlap with another new partition \"%s\"",
5291 : sps1->name->relname, sps2->name->relname),
5292 : parser_errposition(pstate, exprLocation((Node *) val)));
5293 : }
5294 : }
5295 : }
5296 18 : }
5297 :
5298 : /*
5299 : * check_partition_bounds_for_split_range
5300 : *
5301 : * (function for BY RANGE partitioning)
5302 : *
5303 : * Checks that bounds of new partition "spec" are inside bounds of split
5304 : * partition (with Oid splitPartOid). If first=true (this means that "spec" is
5305 : * the first of the new partitions), then the lower bound of "spec" should be
5306 : * equal (or greater than or equal in case defaultPart=true) to the lower
5307 : * bound of the split partition. If last=true (this means that "spec" is the
5308 : * last of the new partitions), then the upper bound of "spec" should be
5309 : * equal (or less than or equal in case defaultPart=true) to the upper bound
5310 : * of the split partition.
5311 : *
5312 : * parent: partitioned table
5313 : * relname: name of the new partition
5314 : * spec: bounds specification of the new partition
5315 : * splitPartOid: split partition Oid
5316 : * first: true iff the new partition "spec" is the first of the
5317 : * new partitions
5318 : * last: true iff the new partition "spec" is the last of the
5319 : * new partitions
5320 : * defaultPart: true iff new partitions contain the DEFAULT partition
5321 : * pstate: pointer to ParseState struct to determine error position
5322 : */
5323 : static void
5324 468 : check_partition_bounds_for_split_range(Relation parent,
5325 : char *relname,
5326 : PartitionBoundSpec *spec,
5327 : Oid splitPartOid,
5328 : bool first,
5329 : bool last,
5330 : bool defaultPart,
5331 : ParseState *pstate)
5332 : {
5333 468 : PartitionKey key = RelationGetPartitionKey(parent);
5334 : PartitionRangeBound *lower,
5335 : *upper;
5336 : int cmpval;
5337 :
5338 : Assert(key->strategy == PARTITION_STRATEGY_RANGE);
5339 : Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
5340 :
5341 468 : lower = make_one_partition_rbound(key, -1, spec->lowerdatums, true);
5342 468 : upper = make_one_partition_rbound(key, -1, spec->upperdatums, false);
5343 :
5344 : /*
5345 : * First, check if the resulting range would be empty with the specified
5346 : * lower and upper bounds. partition_rbound_cmp cannot return zero here,
5347 : * since the lower-bound flags are different.
5348 : */
5349 468 : cmpval = partition_rbound_cmp(key->partnatts,
5350 : key->partsupfunc,
5351 : key->partcollation,
5352 : lower->datums, lower->kind,
5353 : true, upper);
5354 : Assert(cmpval != 0);
5355 468 : if (cmpval > 0)
5356 : {
5357 : /* Point to the problematic key in the lower datums list. */
5358 6 : PartitionRangeDatum *datum = list_nth(spec->lowerdatums, cmpval - 1);
5359 :
5360 6 : ereport(ERROR,
5361 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5362 : errmsg("empty range bound specified for partition \"%s\"",
5363 : relname),
5364 : errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
5365 : get_range_partbound_string(spec->lowerdatums),
5366 : get_range_partbound_string(spec->upperdatums)),
5367 : parser_errposition(pstate, exprLocation((Node *) datum)));
5368 : }
5369 :
5370 : /*
5371 : * Need to check first and last partitions (from the set of new
5372 : * partitions)
5373 : */
5374 462 : if (first || last)
5375 : {
5376 372 : PartitionBoundSpec *split_spec = get_partition_bound_spec(splitPartOid);
5377 : PartitionRangeDatum *datum;
5378 :
5379 372 : if (first)
5380 : {
5381 : PartitionRangeBound *split_lower;
5382 :
5383 198 : split_lower = make_one_partition_rbound(key, -1, split_spec->lowerdatums, true);
5384 :
5385 198 : cmpval = partition_rbound_cmp(key->partnatts,
5386 : key->partsupfunc,
5387 : key->partcollation,
5388 : lower->datums, lower->kind,
5389 : true, split_lower);
5390 198 : if (cmpval != 0)
5391 12 : datum = list_nth(spec->lowerdatums, abs(cmpval) - 1);
5392 :
5393 : /*
5394 : * The lower bound of "spec" must equal the lower bound of the
5395 : * split partition. However, if one of the new partitions is
5396 : * DEFAULT, then it is ok for the new partition's lower bound to
5397 : * be greater than that of the split partition.
5398 : */
5399 198 : if (!defaultPart)
5400 : {
5401 186 : if (cmpval != 0)
5402 12 : ereport(ERROR,
5403 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5404 : errmsg("lower bound of partition \"%s\" is not equal to lower bound of split partition \"%s\"",
5405 : relname,
5406 : get_rel_name(splitPartOid)),
5407 : errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5408 : "ALTER TABLE ... SPLIT PARTITION"),
5409 : parser_errposition(pstate, exprLocation((Node *) datum)));
5410 : }
5411 12 : else if (cmpval < 0)
5412 0 : ereport(ERROR,
5413 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5414 : errmsg("lower bound of partition \"%s\" is less than lower bound of split partition \"%s\"",
5415 : relname,
5416 : get_rel_name(splitPartOid)),
5417 : errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5418 : "ALTER TABLE ... SPLIT PARTITION"),
5419 : parser_errposition(pstate, exprLocation((Node *) datum)));
5420 : }
5421 : else
5422 : {
5423 : PartitionRangeBound *split_upper;
5424 :
5425 174 : split_upper = make_one_partition_rbound(key, -1, split_spec->upperdatums, false);
5426 :
5427 174 : cmpval = partition_rbound_cmp(key->partnatts,
5428 : key->partsupfunc,
5429 : key->partcollation,
5430 : upper->datums, upper->kind,
5431 : false, split_upper);
5432 174 : if (cmpval != 0)
5433 18 : datum = list_nth(spec->upperdatums, abs(cmpval) - 1);
5434 :
5435 : /*
5436 : * The upper bound of "spec" must equal the upper bound of the
5437 : * split partition. However, if one of the new partitions is
5438 : * DEFAULT, then it is ok for the new partition's upper bound to
5439 : * be less than that of the split partition.
5440 : */
5441 174 : if (!defaultPart)
5442 : {
5443 162 : if (cmpval != 0)
5444 12 : ereport(ERROR,
5445 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5446 : errmsg("upper bound of partition \"%s\" is not equal to upper bound of split partition \"%s\"",
5447 : relname,
5448 : get_rel_name(splitPartOid)),
5449 : errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5450 : "ALTER TABLE ... SPLIT PARTITION"),
5451 : parser_errposition(pstate, exprLocation((Node *) datum)));
5452 : }
5453 12 : else if (cmpval > 0)
5454 0 : ereport(ERROR,
5455 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5456 : errmsg("upper bound of partition \"%s\" is greater than upper bound of split partition \"%s\"",
5457 : relname,
5458 : get_rel_name(splitPartOid)),
5459 : errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5460 : "ALTER TABLE ... SPLIT PARTITION"),
5461 : parser_errposition(pstate, exprLocation((Node *) datum)));
5462 : }
5463 : }
5464 438 : }
5465 :
5466 : /*
5467 : * check_partition_bounds_for_split_list
5468 : *
5469 : * (function for BY LIST partitioning)
5470 : *
5471 : * Checks that the bounds of the new partition are inside the bounds of the
5472 : * split partition (with Oid splitPartOid).
5473 : *
5474 : * parent: partitioned table
5475 : * relname: name of the new partition
5476 : * spec: bounds specification of the new partition
5477 : * splitPartOid: split partition Oid
5478 : * pstate: pointer to ParseState struct to determine error position
5479 : */
5480 : static void
5481 108 : check_partition_bounds_for_split_list(Relation parent, char *relname,
5482 : PartitionBoundSpec *spec,
5483 : Oid splitPartOid,
5484 : ParseState *pstate)
5485 : {
5486 108 : PartitionKey key = RelationGetPartitionKey(parent);
5487 108 : PartitionDesc partdesc = RelationGetPartitionDesc(parent, false);
5488 108 : PartitionBoundInfo boundinfo = partdesc->boundinfo;
5489 108 : int with = -1;
5490 108 : bool overlap = false;
5491 108 : int overlap_location = -1;
5492 :
5493 : Assert(key->strategy == PARTITION_STRATEGY_LIST);
5494 : Assert(spec->strategy == PARTITION_STRATEGY_LIST);
5495 : Assert(boundinfo && boundinfo->strategy == PARTITION_STRATEGY_LIST);
5496 :
5497 : /*
5498 : * Search each value of the new partition "spec" in the existing
5499 : * partitions. All of them should be in the split partition (with Oid
5500 : * splitPartOid).
5501 : */
5502 516 : foreach_node(Const, val, spec->listdatums)
5503 : {
5504 330 : overlap_location = exprLocation((Node *) val);
5505 330 : if (!val->constisnull)
5506 : {
5507 : int offset;
5508 : bool equal;
5509 :
5510 318 : offset = partition_list_bsearch(&key->partsupfunc[0],
5511 : key->partcollation,
5512 : boundinfo,
5513 : val->constvalue,
5514 : &equal);
5515 318 : if (offset >= 0 && equal)
5516 : {
5517 312 : with = boundinfo->indexes[offset];
5518 312 : if (partdesc->oids[with] != splitPartOid)
5519 : {
5520 6 : overlap = true;
5521 6 : break;
5522 : }
5523 : }
5524 : else
5525 6 : ereport(ERROR,
5526 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5527 : errmsg("new partition \"%s\" cannot have this value because split partition \"%s\" does not have",
5528 : relname,
5529 : get_rel_name(splitPartOid)),
5530 : parser_errposition(pstate, overlap_location));
5531 : }
5532 12 : else if (partition_bound_accepts_nulls(boundinfo))
5533 : {
5534 6 : with = boundinfo->null_index;
5535 6 : if (partdesc->oids[with] != splitPartOid)
5536 : {
5537 0 : overlap = true;
5538 0 : break;
5539 : }
5540 : }
5541 : else
5542 6 : ereport(ERROR,
5543 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5544 : errmsg("new partition \"%s\" cannot have NULL value because split partition \"%s\" does not have",
5545 : relname,
5546 : get_rel_name(splitPartOid)),
5547 : parser_errposition(pstate, overlap_location));
5548 : }
5549 :
5550 96 : if (overlap)
5551 : {
5552 : Assert(with >= 0);
5553 6 : ereport(ERROR,
5554 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5555 : errmsg("new partition \"%s\" would overlap with another (not split) partition \"%s\"",
5556 : relname, get_rel_name(partdesc->oids[with])),
5557 : parser_errposition(pstate, overlap_location));
5558 : }
5559 90 : }
5560 :
5561 : /*
5562 : * find_value_in_new_partitions_list
5563 : *
5564 : * (function for BY LIST partitioning)
5565 : *
5566 : * Function returns true iff any of the new partitions contains the value
5567 : * "value".
5568 : *
5569 : * partsupfunc: information about the comparison function associated with
5570 : * the partition key
5571 : * partcollation: partitioning collation
5572 : * parts: pointer to an array with new partition descriptions
5573 : * nparts: number of new partitions
5574 : * value: the value that we are looking for
5575 : * isnull: true if the value that we are looking for is NULL
5576 : */
5577 : static bool
5578 90 : find_value_in_new_partitions_list(FmgrInfo *partsupfunc,
5579 : Oid *partcollation,
5580 : SinglePartitionSpec **parts,
5581 : int nparts,
5582 : Datum value,
5583 : bool isnull)
5584 : {
5585 216 : for (int i = 0; i < nparts; i++)
5586 : {
5587 204 : SinglePartitionSpec *sps = parts[i];
5588 :
5589 786 : foreach_node(Const, val, sps->bound->listdatums)
5590 : {
5591 534 : if (isnull && val->constisnull)
5592 78 : return true;
5593 :
5594 528 : if (!isnull && !val->constisnull)
5595 : {
5596 420 : if (DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
5597 : partcollation[0],
5598 : val->constvalue,
5599 : value)) == 0)
5600 72 : return true;
5601 : }
5602 : }
5603 : }
5604 12 : return false;
5605 : }
5606 :
5607 : /*
5608 : * check_parent_values_in_new_partitions
5609 : *
5610 : * (function for BY LIST partitioning)
5611 : *
5612 : * Checks that all values of split partition (with Oid partOid) are contained
5613 : * in new partitions.
5614 : *
5615 : * parent: partitioned table
5616 : * partOid: split partition Oid
5617 : * parts: pointer to an array with new partition descriptions
5618 : * nparts: number of new partitions
5619 : * pstate: pointer to ParseState struct to determine error position
5620 : */
5621 : static void
5622 18 : check_parent_values_in_new_partitions(Relation parent,
5623 : Oid partOid,
5624 : SinglePartitionSpec **parts,
5625 : int nparts,
5626 : ParseState *pstate)
5627 : {
5628 18 : PartitionKey key = RelationGetPartitionKey(parent);
5629 18 : PartitionDesc partdesc = RelationGetPartitionDesc(parent, false);
5630 18 : PartitionBoundInfo boundinfo = partdesc->boundinfo;
5631 : int i;
5632 18 : bool found = true;
5633 18 : Datum datum = PointerGetDatum(NULL);
5634 :
5635 : Assert(key->strategy == PARTITION_STRATEGY_LIST);
5636 :
5637 : /*
5638 : * Special processing for NULL value. Search for a NULL value if the split
5639 : * partition (partOid) contains it.
5640 : */
5641 18 : if (partition_bound_accepts_nulls(boundinfo) &&
5642 12 : partdesc->oids[boundinfo->null_index] == partOid)
5643 : {
5644 12 : if (!find_value_in_new_partitions_list(&key->partsupfunc[0],
5645 : key->partcollation, parts, nparts, datum, true))
5646 6 : found = false;
5647 : }
5648 :
5649 18 : if (!found)
5650 6 : ereport(ERROR,
5651 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5652 : errmsg("new partitions combined partition bounds do not contain value (%s) but split partition \"%s\" does",
5653 : "NULL",
5654 : get_rel_name(partOid)),
5655 : errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5656 : "ALTER TABLE ... SPLIT PARTITION"));
5657 :
5658 : /*
5659 : * Search all values of split partition with partOid in the PartitionDesc
5660 : * of partitioned table.
5661 : */
5662 108 : for (i = 0; i < boundinfo->ndatums; i++)
5663 : {
5664 102 : if (partdesc->oids[boundinfo->indexes[i]] == partOid)
5665 : {
5666 : /* We found the value that the split partition contains. */
5667 78 : datum = boundinfo->datums[i][0];
5668 78 : if (!find_value_in_new_partitions_list(&key->partsupfunc[0],
5669 : key->partcollation, parts, nparts, datum, false))
5670 : {
5671 6 : found = false;
5672 6 : break;
5673 : }
5674 : }
5675 : }
5676 :
5677 12 : if (!found)
5678 : {
5679 : Const *notFoundVal;
5680 :
5681 : /*
5682 : * Make a Const for getting the string representation of the missing
5683 : * value.
5684 : */
5685 6 : notFoundVal = makeConst(key->parttypid[0],
5686 6 : key->parttypmod[0],
5687 6 : key->parttypcoll[0],
5688 6 : key->parttyplen[0],
5689 : datum,
5690 : false, /* isnull */
5691 6 : key->parttypbyval[0]);
5692 :
5693 6 : ereport(ERROR,
5694 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5695 : errmsg("new partitions combined partition bounds do not contain value (%s) but split partition \"%s\" does",
5696 : deparse_expression((Node *) notFoundVal, NIL, false, false),
5697 : get_rel_name(partOid)),
5698 : errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5699 : "ALTER TABLE ... SPLIT PARTITION"));
5700 : }
5701 6 : }
5702 :
5703 : /*
5704 : * check_partitions_for_split
5705 : *
5706 : * Checks new partitions for the SPLIT PARTITION command:
5707 : * 1. Bounds of new partitions should not overlap with new and existing
5708 : * partitions.
5709 : * 2. In the case when new or existing partitions contain the DEFAULT
5710 : * partition, new partitions can have any bounds inside the split partition
5711 : * bound (can be spaces between partition bounds).
5712 : * 3. In case new partitions don't contain the DEFAULT partition and the
5713 : * partitioned table does not have the DEFAULT partition, the following
5714 : * should be true: the sum of the bounds of new partitions should be equal
5715 : & to the bound of the split partition.
5716 : *
5717 : * parent: partitioned table
5718 : * splitPartOid: split partition Oid
5719 : * partlist: list of new partitions after partition split
5720 : * pstate: pointer to ParseState struct for determine error position
5721 : */
5722 : void
5723 300 : check_partitions_for_split(Relation parent,
5724 : Oid splitPartOid,
5725 : List *partlist,
5726 : ParseState *pstate)
5727 : {
5728 : PartitionKey key;
5729 : char strategy;
5730 : Oid defaultPartOid;
5731 : bool isSplitPartDefault;
5732 300 : bool createDefaultPart = false;
5733 300 : int default_index = -1;
5734 : int i;
5735 : SinglePartitionSpec **new_parts;
5736 300 : SinglePartitionSpec *spsPrev = NULL;
5737 :
5738 : /*
5739 : * nparts counts the number of split partitions, but it exclude the
5740 : * default partition.
5741 : */
5742 300 : int nparts = 0;
5743 :
5744 300 : key = RelationGetPartitionKey(parent);
5745 300 : strategy = get_partition_strategy(key);
5746 :
5747 : defaultPartOid =
5748 300 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent, true));
5749 :
5750 : Assert(strategy == PARTITION_STRATEGY_RANGE ||
5751 : strategy == PARTITION_STRATEGY_LIST);
5752 :
5753 : /*
5754 : * Make an array new_parts with new partitions except the DEFAULT
5755 : * partition.
5756 : */
5757 300 : new_parts = palloc0_array(SinglePartitionSpec *, list_length(partlist));
5758 :
5759 : /* isSplitPartDefault flag: is split partition a DEFAULT partition? */
5760 300 : isSplitPartDefault = (defaultPartOid == splitPartOid);
5761 :
5762 1464 : foreach_node(SinglePartitionSpec, sps, partlist)
5763 : {
5764 864 : if (sps->bound->is_default)
5765 66 : default_index = foreach_current_index(sps);
5766 : else
5767 798 : new_parts[nparts++] = sps;
5768 : }
5769 :
5770 : /* An indicator that the DEFAULT partition will be created. */
5771 300 : if (default_index != -1)
5772 : {
5773 66 : createDefaultPart = true;
5774 : Assert(nparts == list_length(partlist) - 1);
5775 : }
5776 :
5777 300 : if (strategy == PARTITION_STRATEGY_RANGE)
5778 : {
5779 : PartitionRangeBound **lower_bounds;
5780 : SinglePartitionSpec **tmp_new_parts;
5781 :
5782 : /*
5783 : * To simplify the check for ranges of new partitions, we need to sort
5784 : * all partitions in ascending order of their bounds (we compare the
5785 : * lower bound only).
5786 : */
5787 252 : lower_bounds = palloc0_array(PartitionRangeBound *, nparts);
5788 :
5789 : /* Create an array of lower bounds. */
5790 912 : for (i = 0; i < nparts; i++)
5791 : {
5792 660 : lower_bounds[i] = make_one_partition_rbound(key, i,
5793 660 : new_parts[i]->bound->lowerdatums, true);
5794 : }
5795 :
5796 : /* Sort the array of lower bounds. */
5797 252 : qsort_arg(lower_bounds, nparts, sizeof(PartitionRangeBound *),
5798 : qsort_partition_rbound_cmp, (void *) key);
5799 :
5800 : /* Reorder the array of partitions. */
5801 252 : tmp_new_parts = new_parts;
5802 252 : new_parts = palloc0_array(SinglePartitionSpec *, nparts);
5803 912 : for (i = 0; i < nparts; i++)
5804 660 : new_parts[i] = tmp_new_parts[lower_bounds[i]->index];
5805 :
5806 252 : pfree(tmp_new_parts);
5807 252 : pfree(lower_bounds);
5808 : }
5809 :
5810 936 : for (i = 0; i < nparts; i++)
5811 : {
5812 714 : SinglePartitionSpec *sps = new_parts[i];
5813 :
5814 714 : if (isSplitPartDefault)
5815 : {
5816 : /*
5817 : * When the split partition is the DEFAULT partition, we can use
5818 : * any free ranges - as when creating a new partition.
5819 : */
5820 138 : check_new_partition_bound(sps->name->relname, parent, sps->bound,
5821 : pstate);
5822 : }
5823 : else
5824 : {
5825 : /*
5826 : * Checks that the bounds of the current partition are inside the
5827 : * bounds of the split partition. For range partitioning: checks
5828 : * that the upper bound of the previous partition is equal to the
5829 : * lower bound of the current partition. For list partitioning:
5830 : * checks that the split partition contains all values of the
5831 : * current partition.
5832 : */
5833 576 : if (strategy == PARTITION_STRATEGY_RANGE)
5834 : {
5835 468 : bool first = (i == 0);
5836 468 : bool last = (i == (nparts - 1));
5837 :
5838 468 : check_partition_bounds_for_split_range(parent, sps->name->relname, sps->bound,
5839 : splitPartOid, first, last,
5840 : createDefaultPart, pstate);
5841 : }
5842 : else
5843 108 : check_partition_bounds_for_split_list(parent, sps->name->relname,
5844 : sps->bound, splitPartOid, pstate);
5845 : }
5846 :
5847 : /* Ranges of new partitions should not overlap. */
5848 666 : if (strategy == PARTITION_STRATEGY_RANGE && spsPrev)
5849 336 : check_two_partitions_bounds_range(parent, spsPrev->name, spsPrev->bound,
5850 : sps->name, sps->bound,
5851 : createDefaultPart,
5852 : false,
5853 : pstate);
5854 :
5855 636 : spsPrev = sps;
5856 : }
5857 :
5858 222 : if (strategy == PARTITION_STRATEGY_LIST)
5859 : {
5860 : /* Values of new partitions should not overlap. */
5861 30 : check_partitions_not_overlap_list(parent, new_parts, nparts,
5862 : pstate);
5863 :
5864 : /*
5865 : * Need to check that all values of the split partition are contained
5866 : * in the new partitions. Skip this check if the DEFAULT partition
5867 : * exists.
5868 : */
5869 18 : if (!createDefaultPart)
5870 18 : check_parent_values_in_new_partitions(parent, splitPartOid,
5871 : new_parts, nparts, pstate);
5872 : }
5873 :
5874 198 : pfree(new_parts);
5875 198 : }
|