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 4176 : get_qual_from_partbound(Relation parent, PartitionBoundSpec *spec)
251 : {
252 4176 : PartitionKey key = RelationGetPartitionKey(parent);
253 4176 : List *my_qual = NIL;
254 :
255 : Assert(key != NULL);
256 :
257 4176 : switch (key->strategy)
258 : {
259 91 : case PARTITION_STRATEGY_HASH:
260 : Assert(spec->strategy == PARTITION_STRATEGY_HASH);
261 91 : my_qual = get_qual_for_hash(parent, spec);
262 91 : break;
263 :
264 1724 : case PARTITION_STRATEGY_LIST:
265 : Assert(spec->strategy == PARTITION_STRATEGY_LIST);
266 1724 : my_qual = get_qual_for_list(parent, spec);
267 1724 : break;
268 :
269 2361 : case PARTITION_STRATEGY_RANGE:
270 : Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
271 2361 : my_qual = get_qual_for_range(parent, spec, false);
272 2361 : break;
273 : }
274 :
275 4176 : 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 12663 : 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 12663 : *mapping = palloc_array(int, nparts);
324 75834 : for (i = 0; i < nparts; i++)
325 63171 : (*mapping)[i] = -1;
326 :
327 12663 : switch (key->strategy)
328 : {
329 532 : case PARTITION_STRATEGY_HASH:
330 532 : return create_hash_bounds(boundspecs, nparts, key, mapping);
331 :
332 5379 : case PARTITION_STRATEGY_LIST:
333 5379 : return create_list_bounds(boundspecs, nparts, key, mapping);
334 :
335 6752 : case PARTITION_STRATEGY_RANGE:
336 6752 : 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 532 : 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 532 : boundinfo = palloc0_object(PartitionBoundInfoData);
358 532 : boundinfo->strategy = key->strategy;
359 : /* No special hash partitions. */
360 532 : boundinfo->null_index = -1;
361 532 : boundinfo->default_index = -1;
362 :
363 532 : hbounds = palloc_array(PartitionHashBound, nparts);
364 :
365 : /* Convert from node to the internal representation */
366 1664 : for (i = 0; i < nparts; i++)
367 : {
368 1132 : PartitionBoundSpec *spec = boundspecs[i];
369 :
370 1132 : if (spec->strategy != PARTITION_STRATEGY_HASH)
371 0 : elog(ERROR, "invalid strategy in partition bound spec");
372 :
373 1132 : hbounds[i].modulus = spec->modulus;
374 1132 : hbounds[i].remainder = spec->remainder;
375 1132 : hbounds[i].index = i;
376 : }
377 :
378 : /* Sort all the bounds in ascending order */
379 532 : qsort(hbounds, nparts, sizeof(PartitionHashBound),
380 : qsort_partition_hbound_cmp);
381 :
382 : /* After sorting, moduli are now stored in ascending order. */
383 532 : greatest_modulus = hbounds[nparts - 1].modulus;
384 :
385 532 : boundinfo->ndatums = nparts;
386 532 : boundinfo->datums = palloc0_array(Datum *, nparts);
387 532 : boundinfo->kind = NULL;
388 532 : boundinfo->interleaved_parts = NULL;
389 532 : boundinfo->nindexes = greatest_modulus;
390 532 : boundinfo->indexes = (int *) palloc(greatest_modulus * sizeof(int));
391 4210 : for (i = 0; i < greatest_modulus; i++)
392 3678 : 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 532 : 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 1664 : for (i = 0; i < nparts; i++)
407 : {
408 1132 : int modulus = hbounds[i].modulus;
409 1132 : int remainder = hbounds[i].remainder;
410 :
411 1132 : boundinfo->datums[i] = &boundDatums[i * 2];
412 1132 : boundinfo->datums[i][0] = Int32GetDatum(modulus);
413 1132 : boundinfo->datums[i][1] = Int32GetDatum(remainder);
414 :
415 2580 : while (remainder < greatest_modulus)
416 : {
417 : /* overlap? */
418 : Assert(boundinfo->indexes[remainder] == -1);
419 1448 : boundinfo->indexes[remainder] = i;
420 1448 : remainder += modulus;
421 : }
422 :
423 1132 : (*mapping)[hbounds[i].index] = i;
424 : }
425 532 : pfree(hbounds);
426 :
427 532 : 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 5379 : get_non_null_list_datum_count(PartitionBoundSpec **boundspecs, int nparts)
436 : {
437 : int i;
438 5379 : int count = 0;
439 :
440 15983 : for (i = 0; i < nparts; i++)
441 : {
442 : ListCell *lc;
443 :
444 26920 : foreach(lc, boundspecs[i]->listdatums)
445 : {
446 16316 : Const *val = lfirst_node(Const, lc);
447 :
448 16316 : if (!val->constisnull)
449 15911 : count++;
450 : }
451 : }
452 :
453 5379 : return count;
454 : }
455 :
456 : /*
457 : * create_list_bounds
458 : * Create a PartitionBoundInfo for a list partitioned table
459 : */
460 : static PartitionBoundInfo
461 5379 : 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 5379 : int next_index = 0;
470 5379 : int default_index = -1;
471 5379 : int null_index = -1;
472 : Datum *boundDatums;
473 :
474 5379 : boundinfo = palloc0_object(PartitionBoundInfoData);
475 5379 : boundinfo->strategy = key->strategy;
476 : /* Will be set correctly below. */
477 5379 : boundinfo->null_index = -1;
478 5379 : boundinfo->default_index = -1;
479 :
480 5379 : ndatums = get_non_null_list_datum_count(boundspecs, nparts);
481 : all_values = (PartitionListValue *)
482 5379 : palloc(ndatums * sizeof(PartitionListValue));
483 :
484 : /* Create a unified list of non-null values across all partitions. */
485 15983 : for (j = 0, i = 0; i < nparts; i++)
486 : {
487 10604 : PartitionBoundSpec *spec = boundspecs[i];
488 : ListCell *c;
489 :
490 10604 : 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 10604 : if (spec->is_default)
499 : {
500 606 : default_index = i;
501 606 : continue;
502 : }
503 :
504 26314 : foreach(c, spec->listdatums)
505 : {
506 16316 : Const *val = lfirst_node(Const, c);
507 :
508 16316 : if (!val->constisnull)
509 : {
510 15911 : all_values[j].index = i;
511 15911 : all_values[j].value = val->constvalue;
512 15911 : 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 405 : if (null_index != -1)
521 0 : elog(ERROR, "found null more than once");
522 405 : 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 5379 : qsort_arg(all_values, ndatums, sizeof(PartitionListValue),
531 : qsort_partition_list_value_cmp, key);
532 :
533 5379 : boundinfo->ndatums = ndatums;
534 5379 : boundinfo->datums = palloc0_array(Datum *, ndatums);
535 5379 : boundinfo->kind = NULL;
536 5379 : boundinfo->interleaved_parts = NULL;
537 5379 : boundinfo->nindexes = ndatums;
538 5379 : 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 5379 : 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 21290 : for (i = 0; i < ndatums; i++)
554 : {
555 15911 : int orig_index = all_values[i].index;
556 :
557 15911 : boundinfo->datums[i] = &boundDatums[i];
558 31822 : boundinfo->datums[i][0] = datumCopy(all_values[i].value,
559 15911 : key->parttypbyval[0],
560 15911 : key->parttyplen[0]);
561 :
562 : /* If the old index has no mapping, assign one */
563 15911 : if ((*mapping)[orig_index] == -1)
564 9862 : (*mapping)[orig_index] = next_index++;
565 :
566 15911 : boundinfo->indexes[i] = (*mapping)[orig_index];
567 : }
568 :
569 5379 : 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 5379 : if (null_index != -1)
580 : {
581 : Assert(null_index >= 0);
582 405 : if ((*mapping)[null_index] == -1)
583 136 : (*mapping)[null_index] = next_index++;
584 405 : boundinfo->null_index = (*mapping)[null_index];
585 : }
586 :
587 : /* Set the canonical value for default_index, if any. */
588 5379 : 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 606 : (*mapping)[default_index] = next_index++;
598 606 : 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 5379 : 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 3269 : if (boundinfo->ndatums +
620 3269 : partition_bound_accepts_nulls(boundinfo) +
621 3269 : partition_bound_has_default(boundinfo) != nparts)
622 : {
623 1420 : 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 10382 : for (i = 0; i < boundinfo->nindexes; i++)
632 : {
633 8962 : int index = boundinfo->indexes[i];
634 :
635 8962 : if (index < last_index)
636 891 : 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 8071 : else if (partition_bound_accepts_nulls(boundinfo) &&
645 2147 : index == boundinfo->null_index)
646 605 : boundinfo->interleaved_parts = bms_add_member(boundinfo->interleaved_parts,
647 : index);
648 :
649 8962 : 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 3269 : if (partition_bound_has_default(boundinfo))
660 533 : 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 5379 : return boundinfo;
668 : }
669 :
670 : /*
671 : * create_range_bounds
672 : * Create a PartitionBoundInfo for a range partitioned table
673 : */
674 : static PartitionBoundInfo
675 6752 : create_range_bounds(PartitionBoundSpec **boundspecs, int nparts,
676 : PartitionKey key, int **mapping)
677 : {
678 : PartitionBoundInfo boundinfo;
679 6752 : PartitionRangeBound **rbounds = NULL;
680 : PartitionRangeBound **all_bounds,
681 : *prev;
682 : int i,
683 : k,
684 : partnatts;
685 6752 : int ndatums = 0;
686 6752 : int default_index = -1;
687 6752 : int next_index = 0;
688 : Datum *boundDatums;
689 : PartitionRangeDatumKind *boundKinds;
690 :
691 6752 : boundinfo = palloc0_object(PartitionBoundInfoData);
692 6752 : boundinfo->strategy = key->strategy;
693 : /* There is no special null-accepting range partition. */
694 6752 : boundinfo->null_index = -1;
695 : /* Will be set correctly below. */
696 6752 : boundinfo->default_index = -1;
697 :
698 6752 : all_bounds = palloc0_array(PartitionRangeBound *, 2 * nparts);
699 :
700 : /* Create a unified list of range bounds across all the partitions. */
701 6752 : ndatums = 0;
702 58187 : for (i = 0; i < nparts; i++)
703 : {
704 51435 : PartitionBoundSpec *spec = boundspecs[i];
705 : PartitionRangeBound *lower,
706 : *upper;
707 :
708 51435 : 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 51435 : if (spec->is_default)
717 : {
718 825 : default_index = i;
719 825 : continue;
720 : }
721 :
722 50610 : lower = make_one_partition_rbound(key, i, spec->lowerdatums, true);
723 50610 : upper = make_one_partition_rbound(key, i, spec->upperdatums, false);
724 50610 : all_bounds[ndatums++] = lower;
725 50610 : 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 6752 : 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 6752 : palloc(ndatums * sizeof(PartitionRangeBound *));
740 6752 : k = 0;
741 6752 : prev = NULL;
742 107972 : for (i = 0; i < ndatums; i++)
743 : {
744 101220 : PartitionRangeBound *cur = all_bounds[i];
745 101220 : bool is_distinct = false;
746 : int j;
747 :
748 : /* Is the current bound distinct from the previous one? */
749 147140 : for (j = 0; j < key->partnatts; j++)
750 : {
751 : Datum cmpval;
752 :
753 104025 : if (prev == NULL || cur->kind[j] != prev->kind[j])
754 : {
755 7612 : is_distinct = true;
756 7612 : 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 96413 : if (cur->kind[j] != PARTITION_RANGE_DATUM_VALUE)
765 124 : break;
766 :
767 96289 : cmpval = FunctionCall2Coll(&key->partsupfunc[j],
768 96289 : key->partcollation[j],
769 96289 : cur->datums[j],
770 96289 : prev->datums[j]);
771 96289 : if (DatumGetInt32(cmpval) != 0)
772 : {
773 50369 : is_distinct = true;
774 50369 : 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 101220 : if (is_distinct)
783 57981 : rbounds[k++] = all_bounds[i];
784 :
785 101220 : prev = cur;
786 : }
787 :
788 6752 : pfree(all_bounds);
789 :
790 : /* Update ndatums to hold the count of distinct datums. */
791 6752 : 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 6752 : boundinfo->ndatums = ndatums;
802 6752 : boundinfo->datums = palloc0_array(Datum *, ndatums);
803 6752 : boundinfo->kind = palloc0_array(PartitionRangeDatumKind *, ndatums);
804 6752 : 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 6752 : boundinfo->nindexes = ndatums + 1;
811 6752 : 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 6752 : partnatts = key->partnatts;
820 6752 : boundDatums = (Datum *) palloc(ndatums * partnatts * sizeof(Datum));
821 6752 : boundKinds = palloc_array(PartitionRangeDatumKind, ndatums * partnatts);
822 :
823 64733 : for (i = 0; i < ndatums; i++)
824 : {
825 : int j;
826 :
827 57981 : boundinfo->datums[i] = &boundDatums[i * partnatts];
828 57981 : boundinfo->kind[i] = &boundKinds[i * partnatts];
829 120255 : for (j = 0; j < partnatts; j++)
830 : {
831 62274 : if (rbounds[i]->kind[j] == PARTITION_RANGE_DATUM_VALUE)
832 60549 : boundinfo->datums[i][j] =
833 60549 : datumCopy(rbounds[i]->datums[j],
834 60549 : key->parttypbyval[j],
835 60549 : key->parttyplen[j]);
836 62274 : 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 57981 : if (rbounds[i]->lower)
848 7371 : boundinfo->indexes[i] = -1;
849 : else
850 : {
851 50610 : int orig_index = rbounds[i]->index;
852 :
853 : /* If the old index has no mapping, assign one */
854 50610 : if ((*mapping)[orig_index] == -1)
855 50610 : (*mapping)[orig_index] = next_index++;
856 :
857 50610 : boundinfo->indexes[i] = (*mapping)[orig_index];
858 : }
859 : }
860 :
861 6752 : pfree(rbounds);
862 :
863 : /* Set the canonical value for default_index, if any. */
864 6752 : if (default_index != -1)
865 : {
866 : Assert(default_index >= 0 && (*mapping)[default_index] == -1);
867 825 : (*mapping)[default_index] = next_index++;
868 825 : boundinfo->default_index = (*mapping)[default_index];
869 : }
870 :
871 : /* The extra -1 element. */
872 : Assert(i == ndatums);
873 6752 : boundinfo->indexes[i] = -1;
874 :
875 : /* All partitions must now have been assigned canonical indexes. */
876 : Assert(next_index == nparts);
877 6752 : 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 5902 : partition_bounds_equal(int partnatts, int16 *parttyplen, bool *parttypbyval,
890 : PartitionBoundInfo b1, PartitionBoundInfo b2)
891 : {
892 : int i;
893 :
894 5902 : if (b1->strategy != b2->strategy)
895 0 : return false;
896 :
897 5902 : if (b1->ndatums != b2->ndatums)
898 185 : return false;
899 :
900 5717 : if (b1->nindexes != b2->nindexes)
901 0 : return false;
902 :
903 5717 : if (b1->null_index != b2->null_index)
904 60 : return false;
905 :
906 5657 : if (b1->default_index != b2->default_index)
907 0 : return false;
908 :
909 : /* For all partition strategies, the indexes[] arrays have to match */
910 31638 : for (i = 0; i < b1->nindexes; i++)
911 : {
912 26021 : if (b1->indexes[i] != b2->indexes[i])
913 40 : return false;
914 : }
915 :
916 : /* Finally, compare the datums[] arrays */
917 5617 : 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 25256 : for (i = 0; i < b1->ndatums; i++)
943 : {
944 : int j;
945 :
946 39593 : for (j = 0; j < partnatts; j++)
947 : {
948 : /* For range partitions, the bounds might not be finite. */
949 19894 : if (b1->kind != NULL)
950 : {
951 : /* The different kinds of bound all differ from each other */
952 18819 : 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 18819 : 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 19894 : if (!datumIsEqual(b1->datums[i][j], b2->datums[i][j],
977 19894 : parttypbyval[j], parttyplen[j]))
978 155 : return false;
979 : }
980 : }
981 : }
982 5462 : 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 12663 : 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 12663 : dest = (PartitionBoundInfo) palloc_object(PartitionBoundInfoData);
1005 :
1006 12663 : dest->strategy = src->strategy;
1007 12663 : ndatums = dest->ndatums = src->ndatums;
1008 12663 : nindexes = dest->nindexes = src->nindexes;
1009 12663 : 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 12663 : dest->datums = palloc_array(Datum *, ndatums);
1015 :
1016 12663 : if (src->kind != NULL && ndatums > 0)
1017 6623 : {
1018 : PartitionRangeDatumKind *boundKinds;
1019 :
1020 : /* only RANGE partition should have a non-NULL kind */
1021 : Assert(key->strategy == PARTITION_STRATEGY_RANGE);
1022 :
1023 6623 : 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 6623 : boundKinds = (PartitionRangeDatumKind *) palloc(ndatums * partnatts *
1032 : sizeof(PartitionRangeDatumKind));
1033 :
1034 64604 : for (i = 0; i < ndatums; i++)
1035 : {
1036 57981 : dest->kind[i] = &boundKinds[i * partnatts];
1037 57981 : memcpy(dest->kind[i], src->kind[i],
1038 : sizeof(PartitionRangeDatumKind) * partnatts);
1039 : }
1040 : }
1041 : else
1042 6040 : dest->kind = NULL;
1043 :
1044 : /* copy interleaved partitions for LIST partitioned tables */
1045 12663 : 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 12663 : if (ndatums > 0)
1052 : {
1053 12445 : bool hash_part = (key->strategy == PARTITION_STRATEGY_HASH);
1054 12445 : int natts = hash_part ? 2 : partnatts;
1055 12445 : Datum *boundDatums = palloc(ndatums * natts * sizeof(Datum));
1056 :
1057 87469 : for (i = 0; i < ndatums; i++)
1058 : {
1059 : int j;
1060 :
1061 75024 : dest->datums[i] = &boundDatums[i * natts];
1062 :
1063 155473 : for (j = 0; j < natts; j++)
1064 : {
1065 80449 : if (dest->kind == NULL ||
1066 62274 : dest->kind[i][j] == PARTITION_RANGE_DATUM_VALUE)
1067 : {
1068 : bool byval;
1069 : int typlen;
1070 :
1071 78724 : if (hash_part)
1072 : {
1073 2264 : typlen = sizeof(int32); /* Always int4 */
1074 2264 : byval = true; /* int4 is pass-by-value */
1075 : }
1076 : else
1077 : {
1078 76460 : byval = key->parttypbyval[j];
1079 76460 : typlen = key->parttyplen[j];
1080 : }
1081 78724 : dest->datums[i][j] = datumCopy(src->datums[i][j],
1082 : byval, typlen);
1083 : }
1084 : }
1085 : }
1086 : }
1087 :
1088 12663 : dest->indexes = palloc_array(int, nindexes);
1089 12663 : memcpy(dest->indexes, src->indexes, sizeof(int) * nindexes);
1090 :
1091 12663 : dest->null_index = src->null_index;
1092 12663 : dest->default_index = src->default_index;
1093 :
1094 12663 : 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 706 : 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 706 : *outer_parts = *inner_parts = NIL;
1130 706 : 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 405 : case PARTITION_STRATEGY_LIST:
1151 405 : return merge_list_bounds(partsupfunc,
1152 : partcollation,
1153 : outer_rel,
1154 : inner_rel,
1155 : jointype,
1156 : outer_parts,
1157 : inner_parts);
1158 :
1159 301 : case PARTITION_STRATEGY_RANGE:
1160 301 : 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 405 : 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 405 : PartitionBoundInfo merged_bounds = NULL;
1198 405 : PartitionBoundInfo outer_bi = outer_rel->boundinfo;
1199 405 : PartitionBoundInfo inner_bi = inner_rel->boundinfo;
1200 405 : bool outer_has_default = partition_bound_has_default(outer_bi);
1201 405 : bool inner_has_default = partition_bound_has_default(inner_bi);
1202 405 : int outer_default = outer_bi->default_index;
1203 405 : int inner_default = inner_bi->default_index;
1204 405 : bool outer_has_null = partition_bound_accepts_nulls(outer_bi);
1205 405 : 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 405 : int next_index = 0;
1211 405 : int null_index = -1;
1212 405 : int default_index = -1;
1213 405 : List *merged_datums = NIL;
1214 405 : 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 405 : init_partition_map(outer_rel, &outer_map);
1224 405 : 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 405 : if (outer_has_default && is_dummy_partition(outer_rel, outer_default))
1231 20 : outer_has_default = false;
1232 405 : 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 405 : outer_pos = inner_pos = 0;
1243 3185 : while (outer_pos < outer_bi->ndatums || inner_pos < inner_bi->ndatums)
1244 : {
1245 2820 : int outer_index = -1;
1246 2820 : int inner_index = -1;
1247 : Datum *outer_datums;
1248 : Datum *inner_datums;
1249 : int cmpval;
1250 2820 : Datum *merged_datum = NULL;
1251 2820 : int merged_index = -1;
1252 :
1253 2820 : 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 2780 : outer_index = outer_bi->indexes[outer_pos];
1260 2780 : if (is_dummy_partition(outer_rel, outer_index))
1261 : {
1262 140 : outer_pos++;
1263 140 : continue;
1264 : }
1265 : }
1266 2680 : 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 2680 : inner_index = inner_bi->indexes[inner_pos];
1273 2680 : 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 5360 : outer_datums = outer_pos < outer_bi->ndatums ?
1282 2680 : outer_bi->datums[outer_pos] : NULL;
1283 5360 : inner_datums = inner_pos < inner_bi->ndatums ?
1284 2680 : 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 2680 : if (outer_pos >= outer_bi->ndatums)
1296 40 : cmpval = 1;
1297 2640 : else if (inner_pos >= inner_bi->ndatums)
1298 0 : cmpval = -1;
1299 : else
1300 : {
1301 : Assert(outer_datums != NULL && inner_datums != NULL);
1302 2640 : cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
1303 : partcollation[0],
1304 : outer_datums[0],
1305 : inner_datums[0]));
1306 : }
1307 :
1308 2680 : 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 1370 : merged_index = merge_matching_partitions(&outer_map, &inner_map,
1321 : outer_index, inner_index,
1322 : &next_index);
1323 1370 : if (merged_index == -1)
1324 25 : goto cleanup;
1325 :
1326 1345 : merged_datum = outer_datums;
1327 :
1328 : /* Move to the next pair of list values. */
1329 1345 : outer_pos++;
1330 1345 : inner_pos++;
1331 : }
1332 1310 : 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 530 : if (inner_has_default || IS_OUTER_JOIN(jointype))
1344 : {
1345 : /* Get the outer partition. */
1346 340 : outer_index = outer_bi->indexes[outer_pos];
1347 : Assert(outer_index >= 0);
1348 340 : 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 340 : if (merged_index == -1)
1358 5 : goto cleanup;
1359 335 : merged_datum = outer_datums;
1360 : }
1361 :
1362 : /* Move to the next list value on the outer side. */
1363 525 : 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 780 : if (outer_has_default || jointype == JOIN_FULL)
1378 : {
1379 : /* Get the inner partition. */
1380 210 : inner_index = inner_bi->indexes[inner_pos];
1381 : Assert(inner_index >= 0);
1382 210 : 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 210 : if (merged_index == -1)
1392 10 : goto cleanup;
1393 200 : merged_datum = inner_datums;
1394 : }
1395 :
1396 : /* Move to the next list value on the inner side. */
1397 770 : 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 2640 : if (merged_index >= 0 && merged_index != default_index)
1405 : {
1406 1820 : merged_datums = lappend(merged_datums, merged_datum);
1407 1820 : 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 525 : if (outer_has_null &&
1416 160 : is_dummy_partition(outer_rel, outer_bi->null_index))
1417 0 : outer_has_null = false;
1418 525 : if (inner_has_null &&
1419 160 : is_dummy_partition(inner_rel, inner_bi->null_index))
1420 0 : inner_has_null = false;
1421 :
1422 : /* Merge the NULL partitions if any. */
1423 365 : if (outer_has_null || inner_has_null)
1424 180 : 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 365 : if (outer_has_default || inner_has_default)
1433 80 : 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 365 : if (next_index > 0)
1442 : {
1443 : /* Fix the merged_indexes list if necessary. */
1444 365 : if (outer_map.did_remapping || inner_map.did_remapping)
1445 : {
1446 : Assert(jointype == JOIN_FULL);
1447 40 : fix_merged_indexes(&outer_map, &inner_map,
1448 : next_index, merged_indexes);
1449 : }
1450 :
1451 : /* Use maps to match partitions from inputs. */
1452 365 : 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 365 : 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 405 : list_free(merged_datums);
1474 405 : list_free(merged_indexes);
1475 405 : free_partition_map(&outer_map);
1476 405 : free_partition_map(&inner_map);
1477 :
1478 405 : 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 301 : 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 301 : PartitionBoundInfo merged_bounds = NULL;
1507 301 : PartitionBoundInfo outer_bi = outer_rel->boundinfo;
1508 301 : PartitionBoundInfo inner_bi = inner_rel->boundinfo;
1509 301 : bool outer_has_default = partition_bound_has_default(outer_bi);
1510 301 : bool inner_has_default = partition_bound_has_default(inner_bi);
1511 301 : int outer_default = outer_bi->default_index;
1512 301 : 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 301 : int next_index = 0;
1524 301 : int default_index = -1;
1525 301 : List *merged_datums = NIL;
1526 301 : List *merged_kinds = NIL;
1527 301 : 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 301 : init_partition_map(outer_rel, &outer_map);
1535 301 : 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 301 : if (outer_has_default && is_dummy_partition(outer_rel, outer_default))
1542 10 : outer_has_default = false;
1543 301 : 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 301 : outer_lb_pos = inner_lb_pos = 0;
1556 301 : outer_index = get_range_partition(outer_rel, outer_bi, &outer_lb_pos,
1557 : &outer_lb, &outer_ub);
1558 301 : inner_index = get_range_partition(inner_rel, inner_bi, &inner_lb_pos,
1559 : &inner_lb, &inner_ub);
1560 1071 : while (outer_index >= 0 || inner_index >= 0)
1561 : {
1562 : bool overlap;
1563 : int ub_cmpval;
1564 : int lb_cmpval;
1565 826 : PartitionRangeBound merged_lb = {-1, NULL, NULL, true};
1566 826 : PartitionRangeBound merged_ub = {-1, NULL, NULL, false};
1567 826 : 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 826 : if (outer_index == -1)
1579 : {
1580 75 : overlap = false;
1581 75 : lb_cmpval = 1;
1582 75 : ub_cmpval = 1;
1583 : }
1584 751 : else if (inner_index == -1)
1585 : {
1586 30 : overlap = false;
1587 30 : lb_cmpval = -1;
1588 30 : ub_cmpval = -1;
1589 : }
1590 : else
1591 721 : 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 826 : 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 691 : 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 691 : 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 691 : save_outer_ub = outer_ub;
1631 691 : save_inner_ub = inner_ub;
1632 :
1633 : /* Move to the next pair of ranges. */
1634 691 : outer_index = get_range_partition(outer_rel, outer_bi, &outer_lb_pos,
1635 : &outer_lb, &outer_ub);
1636 691 : 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 861 : if (ub_cmpval > 0 && inner_index >= 0 &&
1647 170 : compare_range_bounds(partnatts, partsupfuncs, partcollations,
1648 : &save_outer_ub, &inner_lb) > 0)
1649 51 : goto cleanup;
1650 717 : if (ub_cmpval < 0 && outer_index >= 0 &&
1651 56 : compare_range_bounds(partnatts, partsupfuncs, partcollations,
1652 : &outer_lb, &save_inner_ub) < 0)
1653 16 : 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 645 : if ((outer_has_default && (lb_cmpval > 0 || ub_cmpval < 0)) ||
1662 20 : (inner_has_default && (lb_cmpval < 0 || ub_cmpval > 0)))
1663 5 : goto cleanup;
1664 : }
1665 135 : 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 30 : if (inner_has_default || IS_OUTER_JOIN(jointype))
1681 : {
1682 20 : 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 20 : if (merged_index == -1)
1692 0 : goto cleanup;
1693 20 : merged_lb = outer_lb;
1694 20 : merged_ub = outer_ub;
1695 : }
1696 :
1697 : /* Move to the next range on the outer side. */
1698 30 : 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 105 : if (outer_has_default || jointype == JOIN_FULL)
1718 : {
1719 55 : 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 55 : if (merged_index == -1)
1729 5 : goto cleanup;
1730 50 : merged_lb = inner_lb;
1731 50 : merged_ub = inner_ub;
1732 : }
1733 :
1734 : /* Move to the next range on the inner side. */
1735 100 : 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 770 : if (merged_index >= 0 && merged_index != default_index)
1744 680 : 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 245 : if (outer_has_default || inner_has_default)
1752 50 : 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 245 : 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 245 : 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 245 : 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 301 : list_free(merged_datums);
1792 301 : list_free(merged_kinds);
1793 301 : list_free(merged_indexes);
1794 301 : free_partition_map(&outer_map);
1795 301 : free_partition_map(&inner_map);
1796 :
1797 301 : return merged_bounds;
1798 : }
1799 :
1800 : /*
1801 : * init_partition_map
1802 : * Initialize a PartitionMap struct for given relation
1803 : */
1804 : static void
1805 1412 : init_partition_map(RelOptInfo *rel, PartitionMap *map)
1806 : {
1807 1412 : int nparts = rel->nparts;
1808 : int i;
1809 :
1810 1412 : map->nparts = nparts;
1811 1412 : map->merged_indexes = palloc_array(int, nparts);
1812 1412 : map->merged = palloc_array(bool, nparts);
1813 1412 : map->did_remapping = false;
1814 1412 : map->old_indexes = palloc_array(int, nparts);
1815 5732 : for (i = 0; i < nparts; i++)
1816 : {
1817 4320 : map->merged_indexes[i] = map->old_indexes[i] = -1;
1818 4320 : map->merged[i] = false;
1819 : }
1820 1412 : }
1821 :
1822 : /*
1823 : * free_partition_map
1824 : */
1825 : static void
1826 1412 : free_partition_map(PartitionMap *map)
1827 : {
1828 1412 : pfree(map->merged_indexes);
1829 1412 : pfree(map->merged);
1830 1412 : pfree(map->old_indexes);
1831 1412 : }
1832 :
1833 : /*
1834 : * is_dummy_partition --- has partition been proven empty?
1835 : */
1836 : static bool
1837 7624 : is_dummy_partition(RelOptInfo *rel, int part_index)
1838 : {
1839 : RelOptInfo *part_rel;
1840 :
1841 : Assert(part_index >= 0);
1842 7624 : part_rel = rel->part_rels[part_index];
1843 7624 : if (part_rel == NULL || IS_DUMMY_REL(part_rel))
1844 210 : return true;
1845 7414 : 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 2266 : 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 2266 : outer_merged_index = outer_map->merged_indexes[outer_index];
1866 2266 : outer_merged = outer_map->merged[outer_index];
1867 : Assert(inner_index >= 0 && inner_index < inner_map->nparts);
1868 2266 : inner_merged_index = inner_map->merged_indexes[inner_index];
1869 2266 : 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 2266 : 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 550 : if (outer_merged_index == inner_merged_index)
1885 : {
1886 : Assert(outer_merged);
1887 : Assert(inner_merged);
1888 460 : return outer_merged_index;
1889 : }
1890 90 : 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 85 : if (outer_merged_index < inner_merged_index)
1900 : {
1901 45 : outer_map->merged[outer_index] = true;
1902 45 : inner_map->merged_indexes[inner_index] = outer_merged_index;
1903 45 : inner_map->merged[inner_index] = true;
1904 45 : inner_map->did_remapping = true;
1905 45 : inner_map->old_indexes[inner_index] = inner_merged_index;
1906 45 : return outer_merged_index;
1907 : }
1908 : else
1909 : {
1910 40 : inner_map->merged[inner_index] = true;
1911 40 : outer_map->merged_indexes[outer_index] = inner_merged_index;
1912 40 : outer_map->merged[outer_index] = true;
1913 40 : outer_map->did_remapping = true;
1914 40 : outer_map->old_indexes[outer_index] = outer_merged_index;
1915 40 : return inner_merged_index;
1916 : }
1917 : }
1918 5 : 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 1716 : if (outer_merged_index == -1 && inner_merged_index == -1)
1931 : {
1932 1466 : int merged_index = *next_index;
1933 :
1934 : Assert(!outer_merged);
1935 : Assert(!inner_merged);
1936 1466 : outer_map->merged_indexes[outer_index] = merged_index;
1937 1466 : outer_map->merged[outer_index] = true;
1938 1466 : inner_map->merged_indexes[inner_index] = merged_index;
1939 1466 : inner_map->merged[inner_index] = true;
1940 1466 : *next_index = *next_index + 1;
1941 1466 : return merged_index;
1942 : }
1943 250 : if (outer_merged_index >= 0 && !outer_map->merged[outer_index])
1944 : {
1945 : Assert(inner_merged_index == -1);
1946 : Assert(!inner_merged);
1947 220 : inner_map->merged_indexes[inner_index] = outer_merged_index;
1948 220 : inner_map->merged[inner_index] = true;
1949 220 : outer_map->merged[outer_index] = true;
1950 220 : return outer_merged_index;
1951 : }
1952 30 : 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 30 : 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 360 : 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 360 : 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 360 : 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 5 : if (outer_has_default)
2008 0 : return -1;
2009 :
2010 5 : merged_index = merge_matching_partitions(outer_map, inner_map,
2011 : outer_index, inner_default,
2012 : next_index);
2013 5 : if (merged_index == -1)
2014 5 : 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 355 : merged_index = outer_map->merged_indexes[outer_index];
2039 355 : if (merged_index == -1)
2040 335 : merged_index = merge_partition_with_dummy(outer_map, outer_index,
2041 : next_index);
2042 : }
2043 355 : 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 265 : 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 265 : 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 265 : 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 170 : if (inner_has_default)
2090 10 : return -1;
2091 :
2092 160 : merged_index = merge_matching_partitions(outer_map, inner_map,
2093 : outer_default, inner_index,
2094 : next_index);
2095 160 : if (merged_index == -1)
2096 5 : 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 155 : if (IS_OUTER_JOIN(jointype))
2107 : {
2108 : Assert(jointype != JOIN_RIGHT);
2109 90 : if (*default_index == -1)
2110 60 : *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 95 : merged_index = inner_map->merged_indexes[inner_index];
2121 95 : if (merged_index == -1)
2122 95 : merged_index = merge_partition_with_dummy(inner_map, inner_index,
2123 : next_index);
2124 : }
2125 250 : 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 180 : 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 180 : bool consider_outer_null = false;
2152 180 : 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 180 : if (outer_has_null)
2162 : {
2163 : Assert(outer_null >= 0 && outer_null < outer_map->nparts);
2164 160 : if (outer_map->merged_indexes[outer_null] == -1)
2165 70 : consider_outer_null = true;
2166 : }
2167 180 : if (inner_has_null)
2168 : {
2169 : Assert(inner_null >= 0 && inner_null < inner_map->nparts);
2170 160 : if (inner_map->merged_indexes[inner_null] == -1)
2171 100 : consider_inner_null = true;
2172 : }
2173 :
2174 : /* If both flags are set false, we don't need to do anything. */
2175 180 : if (!consider_outer_null && !consider_inner_null)
2176 60 : return;
2177 :
2178 120 : 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 20 : if (IS_OUTER_JOIN(jointype))
2191 : {
2192 : Assert(jointype != JOIN_RIGHT);
2193 10 : *null_index = merge_partition_with_dummy(outer_map, outer_null,
2194 : next_index);
2195 : }
2196 : }
2197 100 : 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 50 : 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 50 : if (IS_OUTER_JOIN(jointype))
2233 : {
2234 : Assert(jointype != JOIN_RIGHT);
2235 40 : *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 130 : 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 130 : int outer_merged_index = -1;
2262 130 : 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 130 : if (outer_has_default)
2268 : {
2269 : Assert(outer_default >= 0 && outer_default < outer_map->nparts);
2270 100 : outer_merged_index = outer_map->merged_indexes[outer_default];
2271 : }
2272 130 : if (inner_has_default)
2273 : {
2274 : Assert(inner_default >= 0 && inner_default < inner_map->nparts);
2275 30 : inner_merged_index = inner_map->merged_indexes[inner_default];
2276 : }
2277 :
2278 130 : 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 100 : if (IS_OUTER_JOIN(jointype))
2289 : {
2290 : Assert(jointype != JOIN_RIGHT);
2291 60 : 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 30 : 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 30 : 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 130 : }
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 440 : merge_partition_with_dummy(PartitionMap *map, int index, int *next_index)
2362 : {
2363 440 : 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 440 : map->merged_indexes[index] = merged_index;
2369 : /* Leave the merged flag alone! */
2370 440 : *next_index = *next_index + 1;
2371 440 : return merged_index;
2372 : }
2373 :
2374 : /*
2375 : * fix_merged_indexes
2376 : * Adjust merged indexes of re-merged partitions
2377 : */
2378 : static void
2379 40 : 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 40 : new_indexes = palloc_array(int, nmerged);
2390 260 : for (i = 0; i < nmerged; i++)
2391 220 : new_indexes[i] = -1;
2392 :
2393 : /* Build the mapping of old merged indexes to new merged indexes. */
2394 40 : if (outer_map->did_remapping)
2395 : {
2396 175 : for (i = 0; i < outer_map->nparts; i++)
2397 : {
2398 135 : merged_index = outer_map->old_indexes[i];
2399 135 : if (merged_index >= 0)
2400 40 : new_indexes[merged_index] = outer_map->merged_indexes[i];
2401 : }
2402 : }
2403 40 : if (inner_map->did_remapping)
2404 : {
2405 175 : for (i = 0; i < inner_map->nparts; i++)
2406 : {
2407 135 : merged_index = inner_map->old_indexes[i];
2408 135 : if (merged_index >= 0)
2409 40 : new_indexes[merged_index] = inner_map->merged_indexes[i];
2410 : }
2411 : }
2412 :
2413 : /* Fix the merged_indexes list using the mapping. */
2414 365 : foreach(lc, merged_indexes)
2415 : {
2416 325 : merged_index = lfirst_int(lc);
2417 : Assert(merged_index >= 0);
2418 325 : if (new_indexes[merged_index] >= 0)
2419 80 : lfirst_int(lc) = new_indexes[merged_index];
2420 : }
2421 :
2422 40 : pfree(new_indexes);
2423 40 : }
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 610 : 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 610 : int outer_nparts = outer_map->nparts;
2439 610 : 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 610 : outer_indexes = palloc_array(int, nmerged);
2450 610 : inner_indexes = palloc_array(int, nmerged);
2451 2330 : for (i = 0; i < nmerged; i++)
2452 1720 : 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 610 : max_nparts = Max(outer_nparts, inner_nparts);
2458 2550 : for (i = 0; i < max_nparts; i++)
2459 : {
2460 1940 : if (i < outer_nparts)
2461 : {
2462 1850 : int merged_index = outer_map->merged_indexes[i];
2463 :
2464 1850 : if (merged_index >= 0)
2465 : {
2466 : Assert(merged_index < nmerged);
2467 1630 : outer_indexes[merged_index] = i;
2468 : }
2469 : }
2470 1940 : if (i < inner_nparts)
2471 : {
2472 1870 : int merged_index = inner_map->merged_indexes[i];
2473 :
2474 1870 : if (merged_index >= 0)
2475 : {
2476 : Assert(merged_index < nmerged);
2477 1600 : inner_indexes[merged_index] = i;
2478 : }
2479 : }
2480 : }
2481 :
2482 : /* Build the list pairs. */
2483 2330 : for (i = 0; i < nmerged; i++)
2484 : {
2485 1720 : int outer_index = outer_indexes[i];
2486 1720 : 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 1720 : if (outer_index == -1 && inner_index == -1)
2495 80 : continue;
2496 :
2497 3270 : *outer_parts = lappend(*outer_parts, outer_index >= 0 ?
2498 1630 : outer_rel->part_rels[outer_index] : NULL);
2499 3240 : *inner_parts = lappend(*inner_parts, inner_index >= 0 ?
2500 1600 : inner_rel->part_rels[inner_index] : NULL);
2501 : }
2502 :
2503 610 : pfree(outer_indexes);
2504 610 : pfree(inner_indexes);
2505 610 : }
2506 :
2507 : /*
2508 : * build_merged_partition_bounds
2509 : * Create a PartitionBoundInfo struct from merged partition bounds
2510 : */
2511 : static PartitionBoundInfo
2512 610 : 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 610 : int ndatums = list_length(merged_datums);
2518 : int pos;
2519 : ListCell *lc;
2520 :
2521 610 : merged_bounds = palloc_object(PartitionBoundInfoData);
2522 610 : merged_bounds->strategy = strategy;
2523 610 : merged_bounds->ndatums = ndatums;
2524 :
2525 610 : merged_bounds->datums = palloc_array(Datum *, ndatums);
2526 610 : pos = 0;
2527 3370 : foreach(lc, merged_datums)
2528 2760 : merged_bounds->datums[pos++] = (Datum *) lfirst(lc);
2529 :
2530 610 : if (strategy == PARTITION_STRATEGY_RANGE)
2531 : {
2532 : Assert(list_length(merged_kinds) == ndatums);
2533 245 : merged_bounds->kind = palloc_array(PartitionRangeDatumKind *, ndatums);
2534 245 : pos = 0;
2535 1300 : foreach(lc, merged_kinds)
2536 1055 : merged_bounds->kind[pos++] = (PartitionRangeDatumKind *) lfirst(lc);
2537 :
2538 : /* There are ndatums+1 indexes in the case of range partitioning. */
2539 245 : merged_indexes = lappend_int(merged_indexes, -1);
2540 245 : ndatums++;
2541 : }
2542 : else
2543 : {
2544 : Assert(strategy == PARTITION_STRATEGY_LIST);
2545 : Assert(merged_kinds == NIL);
2546 365 : merged_bounds->kind = NULL;
2547 : }
2548 :
2549 : /* interleaved_parts is always NULL for join relations. */
2550 610 : merged_bounds->interleaved_parts = NULL;
2551 :
2552 : Assert(list_length(merged_indexes) == ndatums);
2553 610 : merged_bounds->nindexes = ndatums;
2554 610 : merged_bounds->indexes = palloc_array(int, ndatums);
2555 610 : pos = 0;
2556 3615 : foreach(lc, merged_indexes)
2557 3005 : merged_bounds->indexes[pos++] = lfirst_int(lc);
2558 :
2559 610 : merged_bounds->null_index = null_index;
2560 610 : merged_bounds->default_index = default_index;
2561 :
2562 610 : 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 2114 : 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 2154 : part_index = get_range_partition_internal(bi, lb_pos, lb, ub);
2587 2154 : if (part_index == -1)
2588 525 : return -1;
2589 1629 : } while (is_dummy_partition(rel, part_index));
2590 :
2591 1589 : return part_index;
2592 : }
2593 :
2594 : static int
2595 2154 : 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 2154 : if (*lb_pos >= bi->ndatums)
2602 525 : 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 1629 : lb->index = bi->indexes[*lb_pos];
2609 1629 : lb->datums = bi->datums[*lb_pos];
2610 1629 : lb->kind = bi->kind[*lb_pos];
2611 1629 : lb->lower = true;
2612 : /* Set the upper bound. */
2613 1629 : ub->index = bi->indexes[*lb_pos + 1];
2614 1629 : ub->datums = bi->datums[*lb_pos + 1];
2615 1629 : ub->kind = bi->kind[*lb_pos + 1];
2616 1629 : 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 1629 : if (*lb_pos + 2 >= bi->ndatums)
2626 571 : *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 1058 : if (bi->indexes[*lb_pos + 2] < 0)
2635 395 : *lb_pos = *lb_pos + 2;
2636 : else
2637 663 : *lb_pos = *lb_pos + 1;
2638 : }
2639 :
2640 1629 : 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 721 : 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 721 : 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 721 : if (compare_range_bounds(partnatts, partsupfuncs, partcollations,
2680 : outer_lb, inner_ub) > 0)
2681 : {
2682 30 : *lb_cmpval = 1;
2683 30 : *ub_cmpval = 1;
2684 30 : return false;
2685 : }
2686 :
2687 : /* All other cases indicate overlapping partitions. */
2688 691 : *lb_cmpval = compare_range_bounds(partnatts, partsupfuncs, partcollations,
2689 : outer_lb, inner_lb);
2690 691 : *ub_cmpval = compare_range_bounds(partnatts, partsupfuncs, partcollations,
2691 : outer_ub, inner_ub);
2692 691 : 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 691 : 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 691 : switch (jointype)
2720 : {
2721 361 : 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 361 : *merged_lb = (lb_cmpval > 0) ? *outer_lb : *inner_lb;
2731 361 : *merged_ub = (ub_cmpval < 0) ? *outer_ub : *inner_ub;
2732 361 : break;
2733 :
2734 270 : 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 270 : *merged_lb = *outer_lb;
2743 270 : *merged_ub = *outer_ub;
2744 270 : break;
2745 :
2746 60 : 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 60 : *merged_lb = (lb_cmpval < 0) ? *outer_lb : *inner_lb;
2755 60 : *merged_ub = (ub_cmpval > 0) ? *outer_ub : *inner_ub;
2756 60 : break;
2757 :
2758 0 : default:
2759 0 : elog(ERROR, "unrecognized join type: %d", (int) jointype);
2760 : }
2761 691 : }
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 680 : 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 680 : if (!*merged_datums)
2780 : {
2781 : /* First merged partition */
2782 : Assert(!*merged_kinds);
2783 : Assert(!*merged_indexes);
2784 275 : 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 405 : prev_ub.index = llast_int(*merged_indexes);
2796 405 : prev_ub.datums = (Datum *) llast(*merged_datums);
2797 405 : prev_ub.kind = (PartitionRangeDatumKind *) llast(*merged_kinds);
2798 405 : 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 405 : 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 680 : if (cmpval > 0)
2819 : {
2820 480 : *merged_datums = lappend(*merged_datums, merged_lb->datums);
2821 480 : *merged_kinds = lappend(*merged_kinds, merged_lb->kind);
2822 480 : *merged_indexes = lappend_int(*merged_indexes, -1);
2823 : }
2824 :
2825 : /* Add the upper bound and index of the merged partition. */
2826 680 : *merged_datums = lappend(*merged_datums, merged_ub->datums);
2827 680 : *merged_kinds = lappend(*merged_kinds, merged_ub->kind);
2828 680 : *merged_indexes = lappend_int(*merged_indexes, merged_index);
2829 680 : }
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 22256 : partitions_are_ordered(PartitionBoundInfo boundinfo, Bitmapset *live_parts)
2846 : {
2847 : Assert(boundinfo != NULL);
2848 :
2849 22256 : switch (boundinfo->strategy)
2850 : {
2851 12869 : 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 12869 : if (!partition_bound_has_default(boundinfo) ||
2861 1708 : !bms_is_member(boundinfo->default_index, live_parts))
2862 11667 : return true;
2863 1202 : break;
2864 :
2865 8616 : 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 8616 : if (!bms_overlap(live_parts, boundinfo->interleaved_parts))
2872 6585 : return true;
2873 :
2874 2031 : break;
2875 771 : case PARTITION_STRATEGY_HASH:
2876 771 : break;
2877 : }
2878 :
2879 4004 : 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 7827 : check_new_partition_bound(char *relname, Relation parent,
2890 : PartitionBoundSpec *spec, ParseState *pstate)
2891 : {
2892 7827 : PartitionKey key = RelationGetPartitionKey(parent);
2893 7827 : PartitionDesc partdesc = RelationGetPartitionDesc(parent, false);
2894 7827 : PartitionBoundInfo boundinfo = partdesc->boundinfo;
2895 7827 : int with = -1;
2896 7827 : bool overlap = false;
2897 7827 : int overlap_location = -1;
2898 :
2899 7827 : 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 460 : if (boundinfo == NULL || !partition_bound_has_default(boundinfo))
2908 444 : return;
2909 :
2910 : /* Default partition already exists, error out. */
2911 16 : 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 7367 : switch (key->strategy)
2919 : {
2920 456 : case PARTITION_STRATEGY_HASH:
2921 : {
2922 : Assert(spec->strategy == PARTITION_STRATEGY_HASH);
2923 : Assert(spec->remainder >= 0 && spec->remainder < spec->modulus);
2924 :
2925 456 : 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 289 : offset = partition_hash_bsearch(boundinfo,
2950 : spec->modulus,
2951 : spec->remainder);
2952 289 : 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 9 : next_modulus = DatumGetInt32(boundinfo->datums[0][0]);
2962 9 : if (next_modulus % spec->modulus != 0)
2963 4 : 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 280 : prev_modulus = DatumGetInt32(boundinfo->datums[offset][0]);
2980 :
2981 280 : if (spec->modulus % prev_modulus != 0)
2982 4 : 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 276 : 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 20 : next_modulus = DatumGetInt32(boundinfo->datums[offset + 1][0]);
3002 :
3003 20 : if (next_modulus % spec->modulus != 0)
3004 4 : 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 277 : greatest_modulus = boundinfo->nindexes;
3014 277 : 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 277 : if (remainder >= greatest_modulus)
3023 8 : remainder = remainder % greatest_modulus;
3024 :
3025 : /* Check every potentially-conflicting remainder. */
3026 : do
3027 : {
3028 361 : if (boundinfo->indexes[remainder] != -1)
3029 : {
3030 16 : overlap = true;
3031 16 : overlap_location = spec->location;
3032 16 : with = boundinfo->indexes[remainder];
3033 16 : break;
3034 : }
3035 345 : remainder += spec->modulus;
3036 345 : } while (remainder < greatest_modulus);
3037 : }
3038 :
3039 444 : break;
3040 : }
3041 :
3042 3066 : case PARTITION_STRATEGY_LIST:
3043 : {
3044 : Assert(spec->strategy == PARTITION_STRATEGY_LIST);
3045 :
3046 3066 : 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 4168 : foreach(cell, spec->listdatums)
3057 : {
3058 2592 : Const *val = lfirst_node(Const, cell);
3059 :
3060 2592 : overlap_location = val->location;
3061 2592 : if (!val->constisnull)
3062 : {
3063 : int offset;
3064 : bool equal;
3065 :
3066 2495 : offset = partition_list_bsearch(&key->partsupfunc[0],
3067 : key->partcollation,
3068 : boundinfo,
3069 : val->constvalue,
3070 : &equal);
3071 2495 : if (offset >= 0 && equal)
3072 : {
3073 12 : overlap = true;
3074 12 : with = boundinfo->indexes[offset];
3075 12 : break;
3076 : }
3077 : }
3078 97 : else if (partition_bound_accepts_nulls(boundinfo))
3079 : {
3080 4 : overlap = true;
3081 4 : with = boundinfo->null_index;
3082 4 : break;
3083 : }
3084 : }
3085 : }
3086 :
3087 3066 : break;
3088 : }
3089 :
3090 3845 : case PARTITION_STRATEGY_RANGE:
3091 : {
3092 : PartitionRangeBound *lower,
3093 : *upper;
3094 : int cmpval;
3095 :
3096 : Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
3097 3845 : lower = make_one_partition_rbound(key, -1, spec->lowerdatums, true);
3098 3845 : 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 3845 : 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 3845 : if (cmpval > 0)
3113 : {
3114 : /* Point to problematic key in the lower datums list. */
3115 8 : PartitionRangeDatum *datum = list_nth(spec->lowerdatums,
3116 : cmpval - 1);
3117 :
3118 8 : 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 3837 : 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 2383 : offset = partition_range_bsearch(key->partnatts,
3153 : key->partsupfunc,
3154 : key->partcollation,
3155 : boundinfo, lower,
3156 : &cmpval);
3157 :
3158 2383 : 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 2359 : if (offset + 1 < boundinfo->ndatums)
3167 : {
3168 : Datum *datums;
3169 : PartitionRangeDatumKind *kind;
3170 : bool is_lower;
3171 :
3172 60 : datums = boundinfo->datums[offset + 1];
3173 60 : kind = boundinfo->kind[offset + 1];
3174 60 : is_lower = (boundinfo->indexes[offset + 1] == -1);
3175 :
3176 60 : cmpval = partition_rbound_cmp(key->partnatts,
3177 : key->partsupfunc,
3178 : key->partcollation,
3179 : datums, kind,
3180 : is_lower, upper);
3181 60 : if (cmpval < 0)
3182 : {
3183 : /*
3184 : * Point to problematic key in the upper
3185 : * datums list.
3186 : */
3187 : PartitionRangeDatum *datum =
3188 8 : 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 8 : overlap = true;
3196 8 : overlap_location = datum->location;
3197 8 : 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 24 : datum = cmpval == 0 ? linitial(spec->lowerdatums) :
3214 12 : list_nth(spec->lowerdatums, abs(cmpval) - 1);
3215 24 : overlap = true;
3216 24 : overlap_location = datum->location;
3217 24 : with = boundinfo->indexes[offset + 1];
3218 : }
3219 : }
3220 :
3221 3837 : break;
3222 : }
3223 : }
3224 :
3225 7347 : if (overlap)
3226 : {
3227 : Assert(with >= 0);
3228 64 : 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 231 : 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 462 : new_part_constraints = (new_spec->strategy == PARTITION_STRATEGY_LIST)
3253 107 : ? get_qual_for_list(parent, new_spec)
3254 231 : : get_qual_for_range(parent, new_spec, false);
3255 : def_part_constraints =
3256 231 : 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 231 : 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 231 : if (PartConstraintImpliedByRelConstraint(default_rel, def_part_constraints))
3272 : {
3273 9 : ereport(DEBUG1,
3274 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
3275 : RelationGetRelationName(default_rel))));
3276 9 : 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 222 : if (default_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
3284 34 : all_parts = find_all_inheritors(RelationGetRelid(default_rel),
3285 : AccessExclusiveLock, NULL);
3286 : else
3287 188 : all_parts = list_make1_oid(RelationGetRelid(default_rel));
3288 :
3289 526 : foreach(lc, all_parts)
3290 : {
3291 316 : Oid part_relid = lfirst_oid(lc);
3292 : Relation part_rel;
3293 : Expr *partition_constraint;
3294 : EState *estate;
3295 316 : 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 316 : if (part_relid != RelationGetRelid(default_rel))
3304 : {
3305 94 : 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 94 : partition_constraint = make_ands_explicit(def_part_constraints);
3312 : partition_constraint = (Expr *)
3313 94 : 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 94 : if (PartConstraintImpliedByRelConstraint(part_rel,
3322 : def_part_constraints))
3323 : {
3324 5 : ereport(DEBUG1,
3325 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
3326 : RelationGetRelationName(part_rel))));
3327 :
3328 5 : table_close(part_rel, NoLock);
3329 5 : continue;
3330 : }
3331 : }
3332 : else
3333 : {
3334 222 : part_rel = default_rel;
3335 222 : 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 311 : if (part_rel->rd_rel->relkind != RELKIND_RELATION)
3343 : {
3344 34 : 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 34 : if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
3352 0 : table_close(part_rel, NoLock);
3353 :
3354 34 : continue;
3355 : }
3356 :
3357 277 : estate = CreateExecutorState();
3358 :
3359 : /* Build expression execution states for partition check quals */
3360 277 : partqualstate = ExecPrepareExpr(partition_constraint, estate);
3361 :
3362 277 : econtext = GetPerTupleExprContext(estate);
3363 277 : snapshot = RegisterSnapshot(GetLatestSnapshot());
3364 277 : tupslot = table_slot_create(part_rel, &estate->es_tupleTable);
3365 277 : scan = table_beginscan(part_rel, snapshot, 0, NULL,
3366 : SO_NONE);
3367 :
3368 : /*
3369 : * Switch to per-tuple memory context and reset it for each tuple
3370 : * produced, so we don't leak memory.
3371 : */
3372 277 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
3373 :
3374 586 : while (table_scan_getnextslot(scan, ForwardScanDirection, tupslot))
3375 : {
3376 44 : econtext->ecxt_scantuple = tupslot;
3377 :
3378 44 : if (!ExecCheck(partqualstate, econtext))
3379 12 : ereport(ERROR,
3380 : (errcode(ERRCODE_CHECK_VIOLATION),
3381 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
3382 : RelationGetRelationName(default_rel)),
3383 : errtable(default_rel)));
3384 :
3385 32 : ResetExprContext(econtext);
3386 32 : CHECK_FOR_INTERRUPTS();
3387 : }
3388 :
3389 265 : MemoryContextSwitchTo(oldCxt);
3390 265 : table_endscan(scan);
3391 265 : UnregisterSnapshot(snapshot);
3392 265 : ExecDropSingleTupleTableSlot(tupslot);
3393 265 : FreeExecutorState(estate);
3394 :
3395 265 : if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
3396 89 : table_close(part_rel, NoLock); /* keep the lock until commit */
3397 : }
3398 : }
3399 :
3400 : /*
3401 : * get_hash_partition_greatest_modulus
3402 : *
3403 : * Returns the greatest modulus of the hash partition bound.
3404 : * This is no longer used in the core code, but we keep it around
3405 : * in case external modules are using it.
3406 : */
3407 : int
3408 0 : get_hash_partition_greatest_modulus(PartitionBoundInfo bound)
3409 : {
3410 : Assert(bound && bound->strategy == PARTITION_STRATEGY_HASH);
3411 0 : return bound->nindexes;
3412 : }
3413 :
3414 : /*
3415 : * make_one_partition_rbound
3416 : *
3417 : * Return a PartitionRangeBound given a list of PartitionRangeDatum elements
3418 : * and a flag telling whether the bound is lower or not. Made into a function
3419 : * because there are multiple sites that want to use this facility.
3420 : */
3421 : static PartitionRangeBound *
3422 111170 : make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
3423 : {
3424 : PartitionRangeBound *bound;
3425 : ListCell *lc;
3426 : int i;
3427 :
3428 : Assert(datums != NIL);
3429 :
3430 111170 : bound = palloc0_object(PartitionRangeBound);
3431 111170 : bound->index = index;
3432 111170 : bound->datums = palloc0_array(Datum, key->partnatts);
3433 111170 : bound->kind = palloc0_array(PartitionRangeDatumKind, key->partnatts);
3434 111170 : bound->lower = lower;
3435 :
3436 111170 : i = 0;
3437 229624 : foreach(lc, datums)
3438 : {
3439 118454 : PartitionRangeDatum *datum = lfirst_node(PartitionRangeDatum, lc);
3440 :
3441 : /* What's contained in this range datum? */
3442 118454 : bound->kind[i] = datum->kind;
3443 :
3444 118454 : if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
3445 : {
3446 116110 : Const *val = castNode(Const, datum->value);
3447 :
3448 116110 : if (val->constisnull)
3449 0 : elog(ERROR, "invalid range bound datum");
3450 116110 : bound->datums[i] = val->constvalue;
3451 : }
3452 :
3453 118454 : i++;
3454 : }
3455 :
3456 111170 : return bound;
3457 : }
3458 :
3459 : /*
3460 : * partition_rbound_cmp
3461 : *
3462 : * For two range bounds this decides whether the 1st one (specified by
3463 : * datums1, kind1, and lower1) is <, =, or > the bound specified in *b2.
3464 : *
3465 : * 0 is returned if they are equal, otherwise a non-zero integer whose sign
3466 : * indicates the ordering, and whose absolute value gives the 1-based
3467 : * partition key number of the first mismatching column.
3468 : *
3469 : * partnatts, partsupfunc and partcollation give the number of attributes in the
3470 : * bounds to be compared, comparison function to be used and the collations of
3471 : * attributes, respectively.
3472 : *
3473 : * Note that if the values of the two range bounds compare equal, then we take
3474 : * into account whether they are upper or lower bounds, and an upper bound is
3475 : * considered to be smaller than a lower bound. This is important to the way
3476 : * that RelationBuildPartitionDesc() builds the PartitionBoundInfoData
3477 : * structure, which only stores the upper bound of a common boundary between
3478 : * two contiguous partitions.
3479 : */
3480 : static int32
3481 112664 : partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
3482 : Oid *partcollation,
3483 : Datum *datums1, PartitionRangeDatumKind *kind1,
3484 : bool lower1, PartitionRangeBound *b2)
3485 : {
3486 112664 : int32 colnum = 0;
3487 112664 : int32 cmpval = 0; /* placate compiler */
3488 : int i;
3489 112664 : Datum *datums2 = b2->datums;
3490 112664 : PartitionRangeDatumKind *kind2 = b2->kind;
3491 112664 : bool lower2 = b2->lower;
3492 :
3493 163905 : for (i = 0; i < partnatts; i++)
3494 : {
3495 : /* Track column number in case we need it for result */
3496 117071 : colnum++;
3497 :
3498 : /*
3499 : * First, handle cases where the column is unbounded, which should not
3500 : * invoke the comparison procedure, and should not consider any later
3501 : * columns. Note that the PartitionRangeDatumKind enum elements
3502 : * compare the same way as the values they represent.
3503 : */
3504 117071 : if (kind1[i] < kind2[i])
3505 1319 : return -colnum;
3506 115752 : else if (kind1[i] > kind2[i])
3507 84 : return colnum;
3508 115668 : else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
3509 : {
3510 : /*
3511 : * The column bounds are both MINVALUE or both MAXVALUE. No later
3512 : * columns should be considered, but we still need to compare
3513 : * whether they are upper or lower bounds.
3514 : */
3515 172 : break;
3516 : }
3517 :
3518 115496 : cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
3519 115496 : partcollation[i],
3520 115496 : datums1[i],
3521 115496 : datums2[i]));
3522 115496 : if (cmpval != 0)
3523 64255 : break;
3524 : }
3525 :
3526 : /*
3527 : * If the comparison is anything other than equal, we're done. If they
3528 : * compare equal though, we still have to consider whether the boundaries
3529 : * are inclusive or exclusive. Exclusive one is considered smaller of the
3530 : * two.
3531 : */
3532 111261 : if (cmpval == 0 && lower1 != lower2)
3533 45489 : cmpval = lower1 ? 1 : -1;
3534 :
3535 111261 : return cmpval == 0 ? 0 : (cmpval < 0 ? -colnum : colnum);
3536 : }
3537 :
3538 : /*
3539 : * partition_rbound_datum_cmp
3540 : *
3541 : * Return whether range bound (specified in rb_datums and rb_kind)
3542 : * is <, =, or > partition key of tuple (tuple_datums)
3543 : *
3544 : * n_tuple_datums, partsupfunc and partcollation give number of attributes in
3545 : * the bounds to be compared, comparison function to be used and the collations
3546 : * of attributes resp.
3547 : */
3548 : int32
3549 1046791 : partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation,
3550 : const Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
3551 : const Datum *tuple_datums, int n_tuple_datums)
3552 : {
3553 : int i;
3554 1046791 : int32 cmpval = -1;
3555 :
3556 1096857 : for (i = 0; i < n_tuple_datums; i++)
3557 : {
3558 1050801 : if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
3559 34438 : return -1;
3560 1016363 : else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
3561 34582 : return 1;
3562 :
3563 981781 : cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
3564 981781 : partcollation[i],
3565 981781 : rb_datums[i],
3566 981781 : tuple_datums[i]));
3567 981781 : if (cmpval != 0)
3568 931715 : break;
3569 : }
3570 :
3571 977771 : return cmpval;
3572 : }
3573 :
3574 : /*
3575 : * partition_hbound_cmp
3576 : *
3577 : * Compares modulus first, then remainder if modulus is equal.
3578 : */
3579 : static int32
3580 1079 : partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
3581 : {
3582 1079 : if (modulus1 < modulus2)
3583 116 : return -1;
3584 963 : if (modulus1 > modulus2)
3585 40 : return 1;
3586 923 : if (modulus1 == modulus2 && remainder1 != remainder2)
3587 923 : return (remainder1 > remainder2) ? 1 : -1;
3588 0 : return 0;
3589 : }
3590 :
3591 : /*
3592 : * partition_list_bsearch
3593 : * Returns the index of the greatest bound datum that is less than equal
3594 : * to the given value or -1 if all of the bound datums are greater
3595 : *
3596 : * *is_equal is set to true if the bound datum at the returned index is equal
3597 : * to the input value.
3598 : */
3599 : int
3600 106128 : partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation,
3601 : PartitionBoundInfo boundinfo,
3602 : Datum value, bool *is_equal)
3603 : {
3604 : int lo,
3605 : hi,
3606 : mid;
3607 :
3608 106128 : lo = -1;
3609 106128 : hi = boundinfo->ndatums - 1;
3610 215528 : while (lo < hi)
3611 : {
3612 : int32 cmpval;
3613 :
3614 210060 : mid = (lo + hi + 1) / 2;
3615 210060 : cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
3616 : partcollation[0],
3617 210060 : boundinfo->datums[mid][0],
3618 : value));
3619 210060 : if (cmpval <= 0)
3620 : {
3621 178269 : lo = mid;
3622 178269 : *is_equal = (cmpval == 0);
3623 178269 : if (*is_equal)
3624 100660 : break;
3625 : }
3626 : else
3627 31791 : hi = mid - 1;
3628 : }
3629 :
3630 106128 : return lo;
3631 : }
3632 :
3633 : /*
3634 : * partition_range_bsearch
3635 : * Returns the index of the greatest range bound that is less than or
3636 : * equal to the given range bound or -1 if all of the range bounds are
3637 : * greater
3638 : *
3639 : * Upon return from this function, *cmpval is set to 0 if the bound at the
3640 : * returned index matches the input range bound exactly, otherwise a
3641 : * non-zero integer whose sign indicates the ordering, and whose absolute
3642 : * value gives the 1-based partition key number of the first mismatching
3643 : * column.
3644 : */
3645 : static int
3646 2383 : partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
3647 : Oid *partcollation,
3648 : PartitionBoundInfo boundinfo,
3649 : PartitionRangeBound *probe, int32 *cmpval)
3650 : {
3651 : int lo,
3652 : hi,
3653 : mid;
3654 :
3655 2383 : lo = -1;
3656 2383 : hi = boundinfo->ndatums - 1;
3657 9542 : while (lo < hi)
3658 : {
3659 7171 : mid = (lo + hi + 1) / 2;
3660 14342 : *cmpval = partition_rbound_cmp(partnatts, partsupfunc,
3661 : partcollation,
3662 7171 : boundinfo->datums[mid],
3663 7171 : boundinfo->kind[mid],
3664 7171 : (boundinfo->indexes[mid] == -1),
3665 : probe);
3666 7171 : if (*cmpval <= 0)
3667 : {
3668 7079 : lo = mid;
3669 7079 : if (*cmpval == 0)
3670 12 : break;
3671 : }
3672 : else
3673 92 : hi = mid - 1;
3674 : }
3675 :
3676 2383 : return lo;
3677 : }
3678 :
3679 : /*
3680 : * partition_range_datum_bsearch
3681 : * Returns the index of the greatest range bound that is less than or
3682 : * equal to the given tuple or -1 if all of the range bounds are greater
3683 : *
3684 : * *is_equal is set to true if the range bound at the returned index is equal
3685 : * to the input tuple.
3686 : */
3687 : int
3688 350412 : partition_range_datum_bsearch(FmgrInfo *partsupfunc, Oid *partcollation,
3689 : PartitionBoundInfo boundinfo,
3690 : int nvalues, const Datum *values, bool *is_equal)
3691 : {
3692 : int lo,
3693 : hi,
3694 : mid;
3695 :
3696 350412 : lo = -1;
3697 350412 : hi = boundinfo->ndatums - 1;
3698 1075544 : while (lo < hi)
3699 : {
3700 : int32 cmpval;
3701 :
3702 766628 : mid = (lo + hi + 1) / 2;
3703 766628 : cmpval = partition_rbound_datum_cmp(partsupfunc,
3704 : partcollation,
3705 766628 : boundinfo->datums[mid],
3706 766628 : boundinfo->kind[mid],
3707 : values,
3708 : nvalues);
3709 766628 : if (cmpval <= 0)
3710 : {
3711 441807 : lo = mid;
3712 441807 : *is_equal = (cmpval == 0);
3713 :
3714 441807 : if (*is_equal)
3715 41496 : break;
3716 : }
3717 : else
3718 324821 : hi = mid - 1;
3719 : }
3720 :
3721 350412 : return lo;
3722 : }
3723 :
3724 : /*
3725 : * partition_hash_bsearch
3726 : * Returns the index of the greatest (modulus, remainder) pair that is
3727 : * less than or equal to the given (modulus, remainder) pair or -1 if
3728 : * all of them are greater
3729 : */
3730 : int
3731 289 : partition_hash_bsearch(PartitionBoundInfo boundinfo,
3732 : int modulus, int remainder)
3733 : {
3734 : int lo,
3735 : hi,
3736 : mid;
3737 :
3738 289 : lo = -1;
3739 289 : hi = boundinfo->ndatums - 1;
3740 748 : while (lo < hi)
3741 : {
3742 : int32 cmpval,
3743 : bound_modulus,
3744 : bound_remainder;
3745 :
3746 459 : mid = (lo + hi + 1) / 2;
3747 459 : bound_modulus = DatumGetInt32(boundinfo->datums[mid][0]);
3748 459 : bound_remainder = DatumGetInt32(boundinfo->datums[mid][1]);
3749 459 : cmpval = partition_hbound_cmp(bound_modulus, bound_remainder,
3750 : modulus, remainder);
3751 459 : if (cmpval <= 0)
3752 : {
3753 418 : lo = mid;
3754 :
3755 418 : if (cmpval == 0)
3756 0 : break;
3757 : }
3758 : else
3759 41 : hi = mid - 1;
3760 : }
3761 :
3762 289 : return lo;
3763 : }
3764 :
3765 : /*
3766 : * qsort_partition_hbound_cmp
3767 : *
3768 : * Hash bounds are sorted by modulus, then by remainder.
3769 : */
3770 : static int32
3771 620 : qsort_partition_hbound_cmp(const void *a, const void *b)
3772 : {
3773 620 : const PartitionHashBound *h1 = (const PartitionHashBound *) a;
3774 620 : const PartitionHashBound *h2 = (const PartitionHashBound *) b;
3775 :
3776 1240 : return partition_hbound_cmp(h1->modulus, h1->remainder,
3777 620 : h2->modulus, h2->remainder);
3778 : }
3779 :
3780 : /*
3781 : * qsort_partition_list_value_cmp
3782 : *
3783 : * Compare two list partition bound datums.
3784 : */
3785 : static int32
3786 20256 : qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
3787 : {
3788 20256 : Datum val1 = ((const PartitionListValue *) a)->value,
3789 20256 : val2 = ((const PartitionListValue *) b)->value;
3790 20256 : PartitionKey key = (PartitionKey) arg;
3791 :
3792 20256 : return DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
3793 20256 : key->partcollation[0],
3794 : val1, val2));
3795 : }
3796 :
3797 : /*
3798 : * qsort_partition_rbound_cmp
3799 : *
3800 : * Used when sorting range bounds across all range partitions.
3801 : */
3802 : static int32
3803 97229 : qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
3804 : {
3805 97229 : PartitionRangeBound *b1 = (*(PartitionRangeBound *const *) a);
3806 97229 : PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
3807 97229 : PartitionKey key = (PartitionKey) arg;
3808 :
3809 97229 : return compare_range_bounds(key->partnatts, key->partsupfunc,
3810 : key->partcollation,
3811 : b1, b2);
3812 : }
3813 :
3814 : /*
3815 : * get_partition_operator
3816 : *
3817 : * Return oid of the operator of the given strategy for the given partition
3818 : * key column. It is assumed that the partitioning key is of the same type as
3819 : * the chosen partitioning opclass, or at least binary-compatible. In the
3820 : * latter case, *need_relabel is set to true if the opclass is not of a
3821 : * polymorphic type (indicating a RelabelType node needed on top), otherwise
3822 : * false.
3823 : */
3824 : static Oid
3825 10933 : get_partition_operator(PartitionKey key, int col, StrategyNumber strategy,
3826 : bool *need_relabel)
3827 : {
3828 : Oid operoid;
3829 :
3830 : /*
3831 : * Get the operator in the partitioning opfamily using the opclass'
3832 : * declared input type as both left- and righttype.
3833 : */
3834 10933 : operoid = get_opfamily_member(key->partopfamily[col],
3835 10933 : key->partopcintype[col],
3836 10933 : key->partopcintype[col],
3837 : strategy);
3838 10933 : if (!OidIsValid(operoid))
3839 0 : elog(ERROR, "missing operator %d(%u,%u) in partition opfamily %u",
3840 : strategy, key->partopcintype[col], key->partopcintype[col],
3841 : key->partopfamily[col]);
3842 :
3843 : /*
3844 : * If the partition key column is not of the same type as the operator
3845 : * class and not polymorphic, tell caller to wrap the non-Const expression
3846 : * in a RelabelType. This matches what parse_coerce.c does.
3847 : */
3848 21976 : *need_relabel = (key->parttypid[col] != key->partopcintype[col] &&
3849 11039 : key->partopcintype[col] != RECORDOID &&
3850 106 : !IsPolymorphicType(key->partopcintype[col]));
3851 :
3852 10933 : return operoid;
3853 : }
3854 :
3855 : /*
3856 : * make_partition_op_expr
3857 : * Returns an Expr for the given partition key column with arg1 and
3858 : * arg2 as its leftop and rightop, respectively
3859 : */
3860 : static Expr *
3861 10933 : make_partition_op_expr(PartitionKey key, int keynum,
3862 : uint16 strategy, Expr *arg1, Expr *arg2)
3863 : {
3864 : Oid operoid;
3865 10933 : bool need_relabel = false;
3866 10933 : Expr *result = NULL;
3867 :
3868 : /* Get the correct btree operator for this partitioning column */
3869 10933 : operoid = get_partition_operator(key, keynum, strategy, &need_relabel);
3870 :
3871 : /*
3872 : * Chosen operator may be such that the non-Const operand needs to be
3873 : * coerced, so apply the same; see the comment in
3874 : * get_partition_operator().
3875 : */
3876 10933 : if (!IsA(arg1, Const) &&
3877 8122 : (need_relabel ||
3878 8082 : key->partcollation[keynum] != key->parttypcoll[keynum]))
3879 40 : arg1 = (Expr *) makeRelabelType(arg1,
3880 40 : key->partopcintype[keynum],
3881 : -1,
3882 40 : key->partcollation[keynum],
3883 : COERCE_EXPLICIT_CAST);
3884 :
3885 : /* Generate the actual expression */
3886 10933 : switch (key->strategy)
3887 : {
3888 1784 : case PARTITION_STRATEGY_LIST:
3889 : {
3890 1784 : List *elems = (List *) arg2;
3891 1784 : int nelems = list_length(elems);
3892 :
3893 : Assert(nelems >= 1);
3894 : Assert(keynum == 0);
3895 :
3896 2449 : if (nelems > 1 &&
3897 665 : !type_is_array(key->parttypid[keynum]))
3898 661 : {
3899 : ArrayExpr *arrexpr;
3900 : ScalarArrayOpExpr *saopexpr;
3901 :
3902 : /* Construct an ArrayExpr for the right-hand inputs */
3903 661 : arrexpr = makeNode(ArrayExpr);
3904 661 : arrexpr->array_typeid =
3905 661 : get_array_type(key->parttypid[keynum]);
3906 661 : arrexpr->array_collid = key->parttypcoll[keynum];
3907 661 : arrexpr->element_typeid = key->parttypid[keynum];
3908 661 : arrexpr->elements = elems;
3909 661 : arrexpr->multidims = false;
3910 661 : arrexpr->location = -1;
3911 :
3912 : /* Build leftop = ANY (rightop) */
3913 661 : saopexpr = makeNode(ScalarArrayOpExpr);
3914 661 : saopexpr->opno = operoid;
3915 661 : saopexpr->opfuncid = get_opcode(operoid);
3916 661 : saopexpr->hashfuncid = InvalidOid;
3917 661 : saopexpr->negfuncid = InvalidOid;
3918 661 : saopexpr->useOr = true;
3919 661 : saopexpr->inputcollid = key->partcollation[keynum];
3920 661 : saopexpr->args = list_make2(arg1, arrexpr);
3921 661 : saopexpr->location = -1;
3922 :
3923 661 : result = (Expr *) saopexpr;
3924 : }
3925 : else
3926 : {
3927 1123 : List *elemops = NIL;
3928 : ListCell *lc;
3929 :
3930 2250 : foreach(lc, elems)
3931 : {
3932 1127 : Expr *elem = lfirst(lc),
3933 : *elemop;
3934 :
3935 1127 : elemop = make_opclause(operoid,
3936 : BOOLOID,
3937 : false,
3938 : arg1, elem,
3939 : InvalidOid,
3940 1127 : key->partcollation[keynum]);
3941 1127 : elemops = lappend(elemops, elemop);
3942 : }
3943 :
3944 1123 : result = nelems > 1 ? makeBoolExpr(OR_EXPR, elemops, -1) : linitial(elemops);
3945 : }
3946 1784 : break;
3947 : }
3948 :
3949 9149 : case PARTITION_STRATEGY_RANGE:
3950 9149 : result = make_opclause(operoid,
3951 : BOOLOID,
3952 : false,
3953 : arg1, arg2,
3954 : InvalidOid,
3955 9149 : key->partcollation[keynum]);
3956 9149 : break;
3957 :
3958 0 : case PARTITION_STRATEGY_HASH:
3959 : Assert(false);
3960 0 : break;
3961 : }
3962 :
3963 10933 : return result;
3964 : }
3965 :
3966 : /*
3967 : * get_qual_for_hash
3968 : *
3969 : * Returns a CHECK constraint expression to use as a hash partition's
3970 : * constraint, given the parent relation and partition bound structure.
3971 : *
3972 : * The partition constraint for a hash partition is always a call to the
3973 : * built-in function satisfies_hash_partition().
3974 : */
3975 : static List *
3976 91 : get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
3977 : {
3978 91 : PartitionKey key = RelationGetPartitionKey(parent);
3979 : FuncExpr *fexpr;
3980 : Node *relidConst;
3981 : Node *modulusConst;
3982 : Node *remainderConst;
3983 : List *args;
3984 : ListCell *partexprs_item;
3985 : int i;
3986 :
3987 : /* Fixed arguments. */
3988 91 : relidConst = (Node *) makeConst(OIDOID,
3989 : -1,
3990 : InvalidOid,
3991 : sizeof(Oid),
3992 : ObjectIdGetDatum(RelationGetRelid(parent)),
3993 : false,
3994 : true);
3995 :
3996 91 : modulusConst = (Node *) makeConst(INT4OID,
3997 : -1,
3998 : InvalidOid,
3999 : sizeof(int32),
4000 : Int32GetDatum(spec->modulus),
4001 : false,
4002 : true);
4003 :
4004 91 : remainderConst = (Node *) makeConst(INT4OID,
4005 : -1,
4006 : InvalidOid,
4007 : sizeof(int32),
4008 : Int32GetDatum(spec->remainder),
4009 : false,
4010 : true);
4011 :
4012 91 : args = list_make3(relidConst, modulusConst, remainderConst);
4013 91 : partexprs_item = list_head(key->partexprs);
4014 :
4015 : /* Add an argument for each key column. */
4016 198 : for (i = 0; i < key->partnatts; i++)
4017 : {
4018 : Node *keyCol;
4019 :
4020 : /* Left operand */
4021 107 : if (key->partattrs[i] != 0)
4022 : {
4023 104 : keyCol = (Node *) makeVar(1,
4024 104 : key->partattrs[i],
4025 104 : key->parttypid[i],
4026 104 : key->parttypmod[i],
4027 104 : key->parttypcoll[i],
4028 : 0);
4029 : }
4030 : else
4031 : {
4032 3 : keyCol = (Node *) copyObject(lfirst(partexprs_item));
4033 3 : partexprs_item = lnext(key->partexprs, partexprs_item);
4034 : }
4035 :
4036 107 : args = lappend(args, keyCol);
4037 : }
4038 :
4039 91 : fexpr = makeFuncExpr(F_SATISFIES_HASH_PARTITION,
4040 : BOOLOID,
4041 : args,
4042 : InvalidOid,
4043 : InvalidOid,
4044 : COERCE_EXPLICIT_CALL);
4045 :
4046 91 : return list_make1(fexpr);
4047 : }
4048 :
4049 : /*
4050 : * get_qual_for_list
4051 : *
4052 : * Returns an implicit-AND list of expressions to use as a list partition's
4053 : * constraint, given the parent relation and partition bound structure.
4054 : *
4055 : * The function returns NIL for a default partition when it's the only
4056 : * partition since in that case there is no constraint.
4057 : */
4058 : static List *
4059 1831 : get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
4060 : {
4061 1831 : PartitionKey key = RelationGetPartitionKey(parent);
4062 : List *result;
4063 : Expr *keyCol;
4064 : Expr *opexpr;
4065 : NullTest *nulltest;
4066 : ListCell *cell;
4067 1831 : List *elems = NIL;
4068 1831 : bool list_has_null = false;
4069 :
4070 : /*
4071 : * Only single-column list partitioning is supported, so we are worried
4072 : * only about the partition key with index 0.
4073 : */
4074 : Assert(key->partnatts == 1);
4075 :
4076 : /* Construct Var or expression representing the partition column */
4077 1831 : if (key->partattrs[0] != 0)
4078 1756 : keyCol = (Expr *) makeVar(1,
4079 1756 : key->partattrs[0],
4080 1756 : key->parttypid[0],
4081 1756 : key->parttypmod[0],
4082 1756 : key->parttypcoll[0],
4083 : 0);
4084 : else
4085 75 : keyCol = (Expr *) copyObject(linitial(key->partexprs));
4086 :
4087 : /*
4088 : * For default list partition, collect datums for all the partitions. The
4089 : * default partition constraint should check that the partition key is
4090 : * equal to none of those.
4091 : */
4092 1831 : if (spec->is_default)
4093 : {
4094 : int i;
4095 174 : int ndatums = 0;
4096 174 : PartitionDesc pdesc = RelationGetPartitionDesc(parent, false);
4097 174 : PartitionBoundInfo boundinfo = pdesc->boundinfo;
4098 :
4099 174 : if (boundinfo)
4100 : {
4101 174 : ndatums = boundinfo->ndatums;
4102 :
4103 174 : if (partition_bound_accepts_nulls(boundinfo))
4104 32 : list_has_null = true;
4105 : }
4106 :
4107 : /*
4108 : * If default is the only partition, there need not be any partition
4109 : * constraint on it.
4110 : */
4111 174 : if (ndatums == 0 && !list_has_null)
4112 23 : return NIL;
4113 :
4114 799 : for (i = 0; i < ndatums; i++)
4115 : {
4116 : Const *val;
4117 :
4118 : /*
4119 : * Construct Const from known-not-null datum. We must be careful
4120 : * to copy the value, because our result has to be able to outlive
4121 : * the relcache entry we're copying from.
4122 : */
4123 1296 : val = makeConst(key->parttypid[0],
4124 648 : key->parttypmod[0],
4125 648 : key->parttypcoll[0],
4126 648 : key->parttyplen[0],
4127 648 : datumCopy(*boundinfo->datums[i],
4128 648 : key->parttypbyval[0],
4129 648 : key->parttyplen[0]),
4130 : false, /* isnull */
4131 648 : key->parttypbyval[0]);
4132 :
4133 648 : elems = lappend(elems, val);
4134 : }
4135 : }
4136 : else
4137 : {
4138 : /*
4139 : * Create list of Consts for the allowed values, excluding any nulls.
4140 : */
4141 4364 : foreach(cell, spec->listdatums)
4142 : {
4143 2707 : Const *val = lfirst_node(Const, cell);
4144 :
4145 2707 : if (val->constisnull)
4146 60 : list_has_null = true;
4147 : else
4148 2647 : elems = lappend(elems, copyObject(val));
4149 : }
4150 : }
4151 :
4152 1808 : if (elems)
4153 : {
4154 : /*
4155 : * Generate the operator expression from the non-null partition
4156 : * values.
4157 : */
4158 1784 : opexpr = make_partition_op_expr(key, 0, BTEqualStrategyNumber,
4159 : keyCol, (Expr *) elems);
4160 : }
4161 : else
4162 : {
4163 : /*
4164 : * If there are no partition values, we don't need an operator
4165 : * expression.
4166 : */
4167 24 : opexpr = NULL;
4168 : }
4169 :
4170 1808 : if (!list_has_null)
4171 : {
4172 : /*
4173 : * Gin up a "col IS NOT NULL" test that will be ANDed with the main
4174 : * expression. This might seem redundant, but the partition routing
4175 : * machinery needs it.
4176 : */
4177 1716 : nulltest = makeNode(NullTest);
4178 1716 : nulltest->arg = keyCol;
4179 1716 : nulltest->nulltesttype = IS_NOT_NULL;
4180 1716 : nulltest->argisrow = false;
4181 1716 : nulltest->location = -1;
4182 :
4183 1716 : result = opexpr ? list_make2(nulltest, opexpr) : list_make1(nulltest);
4184 : }
4185 : else
4186 : {
4187 : /*
4188 : * Gin up a "col IS NULL" test that will be OR'd with the main
4189 : * expression.
4190 : */
4191 92 : nulltest = makeNode(NullTest);
4192 92 : nulltest->arg = keyCol;
4193 92 : nulltest->nulltesttype = IS_NULL;
4194 92 : nulltest->argisrow = false;
4195 92 : nulltest->location = -1;
4196 :
4197 92 : if (opexpr)
4198 : {
4199 : Expr *or;
4200 :
4201 68 : or = makeBoolExpr(OR_EXPR, list_make2(nulltest, opexpr), -1);
4202 68 : result = list_make1(or);
4203 : }
4204 : else
4205 24 : result = list_make1(nulltest);
4206 : }
4207 :
4208 : /*
4209 : * Note that, in general, applying NOT to a constraint expression doesn't
4210 : * necessarily invert the set of rows it accepts, because NOT (NULL) is
4211 : * NULL. However, the partition constraints we construct here never
4212 : * evaluate to NULL, so applying NOT works as intended.
4213 : */
4214 1808 : if (spec->is_default)
4215 : {
4216 151 : result = list_make1(make_ands_explicit(result));
4217 151 : result = list_make1(makeBoolExpr(NOT_EXPR, result, -1));
4218 : }
4219 :
4220 1808 : return result;
4221 : }
4222 :
4223 : /*
4224 : * get_qual_for_range
4225 : *
4226 : * Returns an implicit-AND list of expressions to use as a range partition's
4227 : * constraint, given the parent relation and partition bound structure.
4228 : *
4229 : * For a multi-column range partition key, say (a, b, c), with (al, bl, cl)
4230 : * as the lower bound tuple and (au, bu, cu) as the upper bound tuple, we
4231 : * generate an expression tree of the following form:
4232 : *
4233 : * (a IS NOT NULL) and (b IS NOT NULL) and (c IS NOT NULL)
4234 : * AND
4235 : * (a > al OR (a = al AND b > bl) OR (a = al AND b = bl AND c >= cl))
4236 : * AND
4237 : * (a < au OR (a = au AND b < bu) OR (a = au AND b = bu AND c < cu))
4238 : *
4239 : * It is often the case that a prefix of lower and upper bound tuples contains
4240 : * the same values, for example, (al = au), in which case, we will emit an
4241 : * expression tree of the following form:
4242 : *
4243 : * (a IS NOT NULL) and (b IS NOT NULL) and (c IS NOT NULL)
4244 : * AND
4245 : * (a = al)
4246 : * AND
4247 : * (b > bl OR (b = bl AND c >= cl))
4248 : * AND
4249 : * (b < bu OR (b = bu AND c < cu))
4250 : *
4251 : * If a bound datum is either MINVALUE or MAXVALUE, these expressions are
4252 : * simplified using the fact that any value is greater than MINVALUE and less
4253 : * than MAXVALUE. So, for example, if cu = MAXVALUE, c < cu is automatically
4254 : * true, and we need not emit any expression for it, and the last line becomes
4255 : *
4256 : * (b < bu) OR (b = bu), which is simplified to (b <= bu)
4257 : *
4258 : * In most common cases with only one partition column, say a, the following
4259 : * expression tree will be generated: a IS NOT NULL AND a >= al AND a < au
4260 : *
4261 : * For default partition, it returns the negation of the constraints of all
4262 : * the other partitions.
4263 : *
4264 : * External callers should pass for_default as false; we set it to true only
4265 : * when recursing.
4266 : */
4267 : static List *
4268 2930 : get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
4269 : bool for_default)
4270 : {
4271 2930 : List *result = NIL;
4272 : ListCell *cell1,
4273 : *cell2,
4274 : *partexprs_item,
4275 : *partexprs_item_saved;
4276 : int i,
4277 : j;
4278 : PartitionRangeDatum *ldatum,
4279 : *udatum;
4280 2930 : PartitionKey key = RelationGetPartitionKey(parent);
4281 : Expr *keyCol;
4282 : Const *lower_val,
4283 : *upper_val;
4284 : List *lower_or_arms,
4285 : *upper_or_arms;
4286 : int num_or_arms,
4287 : current_or_arm;
4288 : ListCell *lower_or_start_datum,
4289 : *upper_or_start_datum;
4290 : bool need_next_lower_arm,
4291 : need_next_upper_arm;
4292 :
4293 2930 : if (spec->is_default)
4294 : {
4295 223 : List *or_expr_args = NIL;
4296 223 : PartitionDesc pdesc = RelationGetPartitionDesc(parent, false);
4297 223 : Oid *inhoids = pdesc->oids;
4298 223 : int nparts = pdesc->nparts,
4299 : k;
4300 :
4301 891 : for (k = 0; k < nparts; k++)
4302 : {
4303 668 : Oid inhrelid = inhoids[k];
4304 : HeapTuple tuple;
4305 : Datum datum;
4306 : PartitionBoundSpec *bspec;
4307 :
4308 668 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(inhrelid));
4309 668 : if (!HeapTupleIsValid(tuple))
4310 0 : elog(ERROR, "cache lookup failed for relation %u", inhrelid);
4311 :
4312 668 : datum = SysCacheGetAttrNotNull(RELOID, tuple,
4313 : Anum_pg_class_relpartbound);
4314 : bspec = (PartitionBoundSpec *)
4315 668 : stringToNode(TextDatumGetCString(datum));
4316 668 : if (!IsA(bspec, PartitionBoundSpec))
4317 0 : elog(ERROR, "expected PartitionBoundSpec");
4318 :
4319 668 : if (!bspec->is_default)
4320 : {
4321 : List *part_qual;
4322 :
4323 445 : part_qual = get_qual_for_range(parent, bspec, true);
4324 :
4325 : /*
4326 : * AND the constraints of the partition and add to
4327 : * or_expr_args
4328 : */
4329 890 : or_expr_args = lappend(or_expr_args, list_length(part_qual) > 1
4330 433 : ? makeBoolExpr(AND_EXPR, part_qual, -1)
4331 12 : : linitial(part_qual));
4332 : }
4333 668 : ReleaseSysCache(tuple);
4334 : }
4335 :
4336 223 : if (or_expr_args != NIL)
4337 : {
4338 : Expr *other_parts_constr;
4339 :
4340 : /*
4341 : * Combine the constraints obtained for non-default partitions
4342 : * using OR. As requested, each of the OR's args doesn't include
4343 : * the NOT NULL test for partition keys (which is to avoid its
4344 : * useless repetition). Add the same now.
4345 : */
4346 : other_parts_constr =
4347 350 : makeBoolExpr(AND_EXPR,
4348 : lappend(get_range_nulltest(key),
4349 175 : list_length(or_expr_args) > 1
4350 124 : ? makeBoolExpr(OR_EXPR, or_expr_args,
4351 : -1)
4352 51 : : linitial(or_expr_args)),
4353 : -1);
4354 :
4355 : /*
4356 : * Finally, the default partition contains everything *NOT*
4357 : * contained in the non-default partitions.
4358 : */
4359 175 : result = list_make1(makeBoolExpr(NOT_EXPR,
4360 : list_make1(other_parts_constr), -1));
4361 : }
4362 :
4363 223 : return result;
4364 : }
4365 :
4366 : /*
4367 : * If it is the recursive call for default, we skip the get_range_nulltest
4368 : * to avoid accumulating the NullTest on the same keys for each partition.
4369 : */
4370 2707 : if (!for_default)
4371 2262 : result = get_range_nulltest(key);
4372 :
4373 : /*
4374 : * Iterate over the key columns and check if the corresponding lower and
4375 : * upper datums are equal using the btree equality operator for the
4376 : * column's type. If equal, we emit single keyCol = common_value
4377 : * expression. Starting from the first column for which the corresponding
4378 : * lower and upper bound datums are not equal, we generate OR expressions
4379 : * as shown in the function's header comment.
4380 : */
4381 2707 : i = 0;
4382 2707 : partexprs_item = list_head(key->partexprs);
4383 2707 : partexprs_item_saved = partexprs_item; /* placate compiler */
4384 3067 : forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
4385 : {
4386 : EState *estate;
4387 : MemoryContext oldcxt;
4388 : Expr *test_expr;
4389 : ExprState *test_exprstate;
4390 : Datum test_result;
4391 : bool isNull;
4392 :
4393 3067 : ldatum = lfirst_node(PartitionRangeDatum, cell1);
4394 3067 : udatum = lfirst_node(PartitionRangeDatum, cell2);
4395 :
4396 : /*
4397 : * Since get_range_key_properties() modifies partexprs_item, and we
4398 : * might need to start over from the previous expression in the later
4399 : * part of this function, save away the current value.
4400 : */
4401 3067 : partexprs_item_saved = partexprs_item;
4402 :
4403 3067 : get_range_key_properties(key, i, ldatum, udatum,
4404 : &partexprs_item,
4405 : &keyCol,
4406 : &lower_val, &upper_val);
4407 :
4408 : /*
4409 : * If either value is NULL, the corresponding partition bound is
4410 : * either MINVALUE or MAXVALUE, and we treat them as unequal, because
4411 : * even if they're the same, there is no common value to equate the
4412 : * key column with.
4413 : */
4414 3067 : if (!lower_val || !upper_val)
4415 : break;
4416 :
4417 : /* Create the test expression */
4418 2811 : estate = CreateExecutorState();
4419 2811 : oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
4420 2811 : test_expr = make_partition_op_expr(key, i, BTEqualStrategyNumber,
4421 : (Expr *) lower_val,
4422 : (Expr *) upper_val);
4423 2811 : fix_opfuncids((Node *) test_expr);
4424 2811 : test_exprstate = ExecInitExpr(test_expr, NULL);
4425 2811 : test_result = ExecEvalExprSwitchContext(test_exprstate,
4426 2811 : GetPerTupleExprContext(estate),
4427 : &isNull);
4428 2811 : MemoryContextSwitchTo(oldcxt);
4429 2811 : FreeExecutorState(estate);
4430 :
4431 : /* If not equal, go generate the OR expressions */
4432 2811 : if (!DatumGetBool(test_result))
4433 2451 : break;
4434 :
4435 : /*
4436 : * The bounds for the last key column can't be equal, because such a
4437 : * range partition would never be allowed to be defined (it would have
4438 : * an empty range otherwise).
4439 : */
4440 360 : if (i == key->partnatts - 1)
4441 0 : elog(ERROR, "invalid range bound specification");
4442 :
4443 : /* Equal, so generate keyCol = lower_val expression */
4444 360 : result = lappend(result,
4445 360 : make_partition_op_expr(key, i, BTEqualStrategyNumber,
4446 : keyCol, (Expr *) lower_val));
4447 :
4448 360 : i++;
4449 : }
4450 :
4451 : /* First pair of lower_val and upper_val that are not equal. */
4452 2707 : lower_or_start_datum = cell1;
4453 2707 : upper_or_start_datum = cell2;
4454 :
4455 : /* OR will have as many arms as there are key columns left. */
4456 2707 : num_or_arms = key->partnatts - i;
4457 2707 : current_or_arm = 0;
4458 2707 : lower_or_arms = upper_or_arms = NIL;
4459 2707 : need_next_lower_arm = need_next_upper_arm = true;
4460 2934 : while (current_or_arm < num_or_arms)
4461 : {
4462 2934 : List *lower_or_arm_args = NIL,
4463 2934 : *upper_or_arm_args = NIL;
4464 :
4465 : /* Restart scan of columns from the i'th one */
4466 2934 : j = i;
4467 2934 : partexprs_item = partexprs_item_saved;
4468 :
4469 3217 : for_both_cell(cell1, spec->lowerdatums, lower_or_start_datum,
4470 : cell2, spec->upperdatums, upper_or_start_datum)
4471 : {
4472 3217 : PartitionRangeDatum *ldatum_next = NULL,
4473 3217 : *udatum_next = NULL;
4474 :
4475 3217 : ldatum = lfirst_node(PartitionRangeDatum, cell1);
4476 3217 : if (lnext(spec->lowerdatums, cell1))
4477 562 : ldatum_next = castNode(PartitionRangeDatum,
4478 : lfirst(lnext(spec->lowerdatums, cell1)));
4479 3217 : udatum = lfirst_node(PartitionRangeDatum, cell2);
4480 3217 : if (lnext(spec->upperdatums, cell2))
4481 562 : udatum_next = castNode(PartitionRangeDatum,
4482 : lfirst(lnext(spec->upperdatums, cell2)));
4483 3217 : get_range_key_properties(key, j, ldatum, udatum,
4484 : &partexprs_item,
4485 : &keyCol,
4486 : &lower_val, &upper_val);
4487 :
4488 3217 : if (need_next_lower_arm && lower_val)
4489 : {
4490 : uint16 strategy;
4491 :
4492 : /*
4493 : * For the non-last columns of this arm, use the EQ operator.
4494 : * For the last column of this arm, use GT, unless this is the
4495 : * last column of the whole bound check, or the next bound
4496 : * datum is MINVALUE, in which case use GE.
4497 : */
4498 3019 : if (j - i < current_or_arm)
4499 247 : strategy = BTEqualStrategyNumber;
4500 2772 : else if (j == key->partnatts - 1 ||
4501 239 : (ldatum_next &&
4502 239 : ldatum_next->kind == PARTITION_RANGE_DATUM_MINVALUE))
4503 2561 : strategy = BTGreaterEqualStrategyNumber;
4504 : else
4505 211 : strategy = BTGreaterStrategyNumber;
4506 :
4507 3019 : lower_or_arm_args = lappend(lower_or_arm_args,
4508 3019 : make_partition_op_expr(key, j,
4509 : strategy,
4510 : keyCol,
4511 : (Expr *) lower_val));
4512 : }
4513 :
4514 3217 : if (need_next_upper_arm && upper_val)
4515 : {
4516 : uint16 strategy;
4517 :
4518 : /*
4519 : * For the non-last columns of this arm, use the EQ operator.
4520 : * For the last column of this arm, use LT, unless the next
4521 : * bound datum is MAXVALUE, in which case use LE.
4522 : */
4523 2959 : if (j - i < current_or_arm)
4524 203 : strategy = BTEqualStrategyNumber;
4525 2756 : else if (udatum_next &&
4526 215 : udatum_next->kind == PARTITION_RANGE_DATUM_MAXVALUE)
4527 20 : strategy = BTLessEqualStrategyNumber;
4528 : else
4529 2736 : strategy = BTLessStrategyNumber;
4530 :
4531 2959 : upper_or_arm_args = lappend(upper_or_arm_args,
4532 2959 : make_partition_op_expr(key, j,
4533 : strategy,
4534 : keyCol,
4535 : (Expr *) upper_val));
4536 : }
4537 :
4538 : /*
4539 : * Did we generate enough of OR's arguments? First arm considers
4540 : * the first of the remaining columns, second arm considers first
4541 : * two of the remaining columns, and so on.
4542 : */
4543 3217 : ++j;
4544 3217 : if (j - i > current_or_arm)
4545 : {
4546 : /*
4547 : * We must not emit any more arms if the new column that will
4548 : * be considered is unbounded, or this one was.
4549 : */
4550 2934 : if (!lower_val || !ldatum_next ||
4551 239 : ldatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
4552 2731 : need_next_lower_arm = false;
4553 2934 : if (!upper_val || !udatum_next ||
4554 215 : udatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
4555 2763 : need_next_upper_arm = false;
4556 2934 : break;
4557 : }
4558 : }
4559 :
4560 2934 : if (lower_or_arm_args != NIL)
4561 5544 : lower_or_arms = lappend(lower_or_arms,
4562 2772 : list_length(lower_or_arm_args) > 1
4563 203 : ? makeBoolExpr(AND_EXPR, lower_or_arm_args, -1)
4564 2569 : : linitial(lower_or_arm_args));
4565 :
4566 2934 : if (upper_or_arm_args != NIL)
4567 5512 : upper_or_arms = lappend(upper_or_arms,
4568 2756 : list_length(upper_or_arm_args) > 1
4569 171 : ? makeBoolExpr(AND_EXPR, upper_or_arm_args, -1)
4570 2585 : : linitial(upper_or_arm_args));
4571 :
4572 : /* If no work to do in the next iteration, break away. */
4573 2934 : if (!need_next_lower_arm && !need_next_upper_arm)
4574 2707 : break;
4575 :
4576 227 : ++current_or_arm;
4577 : }
4578 :
4579 : /*
4580 : * Generate the OR expressions for each of lower and upper bounds (if
4581 : * required), and append to the list of implicitly ANDed list of
4582 : * expressions.
4583 : */
4584 2707 : if (lower_or_arms != NIL)
4585 5138 : result = lappend(result,
4586 2569 : list_length(lower_or_arms) > 1
4587 159 : ? makeBoolExpr(OR_EXPR, lower_or_arms, -1)
4588 2410 : : linitial(lower_or_arms));
4589 2707 : if (upper_or_arms != NIL)
4590 5170 : result = lappend(result,
4591 2585 : list_length(upper_or_arms) > 1
4592 139 : ? makeBoolExpr(OR_EXPR, upper_or_arms, -1)
4593 2446 : : linitial(upper_or_arms));
4594 :
4595 : /*
4596 : * As noted above, for non-default, we return list with constant TRUE. If
4597 : * the result is NIL during the recursive call for default, it implies
4598 : * this is the only other partition which can hold every value of the key
4599 : * except NULL. Hence we return the NullTest result skipped earlier.
4600 : */
4601 2707 : if (result == NIL)
4602 0 : result = for_default
4603 0 : ? get_range_nulltest(key)
4604 0 : : list_make1(makeBoolConst(true, false));
4605 :
4606 2707 : return result;
4607 : }
4608 :
4609 : /*
4610 : * get_range_key_properties
4611 : * Returns range partition key information for a given column
4612 : *
4613 : * This is a subroutine for get_qual_for_range, and its API is pretty
4614 : * specialized to that caller.
4615 : *
4616 : * Constructs an Expr for the key column (returned in *keyCol) and Consts
4617 : * for the lower and upper range limits (returned in *lower_val and
4618 : * *upper_val). For MINVALUE/MAXVALUE limits, NULL is returned instead of
4619 : * a Const. All of these structures are freshly palloc'd.
4620 : *
4621 : * *partexprs_item points to the cell containing the next expression in
4622 : * the key->partexprs list, or NULL. It may be advanced upon return.
4623 : */
4624 : static void
4625 6284 : get_range_key_properties(PartitionKey key, int keynum,
4626 : PartitionRangeDatum *ldatum,
4627 : PartitionRangeDatum *udatum,
4628 : ListCell **partexprs_item,
4629 : Expr **keyCol,
4630 : Const **lower_val, Const **upper_val)
4631 : {
4632 : /* Get partition key expression for this column */
4633 6284 : if (key->partattrs[keynum] != 0)
4634 : {
4635 5713 : *keyCol = (Expr *) makeVar(1,
4636 5713 : key->partattrs[keynum],
4637 5713 : key->parttypid[keynum],
4638 5713 : key->parttypmod[keynum],
4639 5713 : key->parttypcoll[keynum],
4640 : 0);
4641 : }
4642 : else
4643 : {
4644 571 : if (*partexprs_item == NULL)
4645 0 : elog(ERROR, "wrong number of partition key expressions");
4646 571 : *keyCol = copyObject(lfirst(*partexprs_item));
4647 571 : *partexprs_item = lnext(key->partexprs, *partexprs_item);
4648 : }
4649 :
4650 : /* Get appropriate Const nodes for the bounds */
4651 6284 : if (ldatum->kind == PARTITION_RANGE_DATUM_VALUE)
4652 5960 : *lower_val = castNode(Const, copyObject(ldatum->value));
4653 : else
4654 324 : *lower_val = NULL;
4655 :
4656 6284 : if (udatum->kind == PARTITION_RANGE_DATUM_VALUE)
4657 5920 : *upper_val = castNode(Const, copyObject(udatum->value));
4658 : else
4659 364 : *upper_val = NULL;
4660 6284 : }
4661 :
4662 : /*
4663 : * get_range_nulltest
4664 : *
4665 : * A non-default range partition table does not currently allow partition
4666 : * keys to be null, so emit an IS NOT NULL expression for each key column.
4667 : */
4668 : static List *
4669 2437 : get_range_nulltest(PartitionKey key)
4670 : {
4671 2437 : List *result = NIL;
4672 : NullTest *nulltest;
4673 : ListCell *partexprs_item;
4674 : int i;
4675 :
4676 2437 : partexprs_item = list_head(key->partexprs);
4677 5425 : for (i = 0; i < key->partnatts; i++)
4678 : {
4679 : Expr *keyCol;
4680 :
4681 2988 : if (key->partattrs[i] != 0)
4682 : {
4683 2711 : keyCol = (Expr *) makeVar(1,
4684 2711 : key->partattrs[i],
4685 2711 : key->parttypid[i],
4686 2711 : key->parttypmod[i],
4687 2711 : key->parttypcoll[i],
4688 : 0);
4689 : }
4690 : else
4691 : {
4692 277 : if (partexprs_item == NULL)
4693 0 : elog(ERROR, "wrong number of partition key expressions");
4694 277 : keyCol = copyObject(lfirst(partexprs_item));
4695 277 : partexprs_item = lnext(key->partexprs, partexprs_item);
4696 : }
4697 :
4698 2988 : nulltest = makeNode(NullTest);
4699 2988 : nulltest->arg = keyCol;
4700 2988 : nulltest->nulltesttype = IS_NOT_NULL;
4701 2988 : nulltest->argisrow = false;
4702 2988 : nulltest->location = -1;
4703 2988 : result = lappend(result, nulltest);
4704 : }
4705 :
4706 2437 : return result;
4707 : }
4708 :
4709 : /*
4710 : * compute_partition_hash_value
4711 : *
4712 : * Compute the hash value for given partition key values.
4713 : */
4714 : uint64
4715 107154 : compute_partition_hash_value(int partnatts, FmgrInfo *partsupfunc, const Oid *partcollation,
4716 : const Datum *values, const bool *isnull)
4717 : {
4718 : int i;
4719 107154 : uint64 rowHash = 0;
4720 107154 : Datum seed = UInt64GetDatum(HASH_PARTITION_SEED);
4721 :
4722 215064 : for (i = 0; i < partnatts; i++)
4723 : {
4724 : /* Nulls are just ignored */
4725 107918 : if (!isnull[i])
4726 : {
4727 : Datum hash;
4728 :
4729 : Assert(OidIsValid(partsupfunc[i].fn_oid));
4730 :
4731 : /*
4732 : * Compute hash for each datum value by calling respective
4733 : * datatype-specific hash functions of each partition key
4734 : * attribute.
4735 : */
4736 107420 : hash = FunctionCall2Coll(&partsupfunc[i], partcollation[i],
4737 107420 : values[i], seed);
4738 :
4739 : /* Form a single 64-bit hash value */
4740 107412 : rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
4741 : }
4742 : }
4743 :
4744 107146 : return rowHash;
4745 : }
4746 :
4747 : /*
4748 : * satisfies_hash_partition
4749 : *
4750 : * This is an SQL-callable function for use in hash partition constraints.
4751 : * The first three arguments are the parent table OID, modulus, and remainder.
4752 : * The remaining arguments are the value of the partitioning columns (or
4753 : * expressions); these are hashed and the results are combined into a single
4754 : * hash value by calling hash_combine64.
4755 : *
4756 : * Returns true if remainder produced when this computed single hash value is
4757 : * divided by the given modulus is equal to given remainder, otherwise false.
4758 : * NB: it's important that this never return null, as the constraint machinery
4759 : * would consider that to be a "pass".
4760 : *
4761 : * See get_qual_for_hash() for usage.
4762 : */
4763 : Datum
4764 1555 : satisfies_hash_partition(PG_FUNCTION_ARGS)
4765 : {
4766 : typedef struct ColumnsHashData
4767 : {
4768 : Oid relid;
4769 : int nkeys;
4770 : Oid variadic_type;
4771 : int16 variadic_typlen;
4772 : bool variadic_typbyval;
4773 : char variadic_typalign;
4774 : Oid partcollid[PARTITION_MAX_KEYS];
4775 : FmgrInfo partsupfunc[FLEXIBLE_ARRAY_MEMBER];
4776 : } ColumnsHashData;
4777 : Oid parentId;
4778 : int modulus;
4779 : int remainder;
4780 1555 : Datum seed = UInt64GetDatum(HASH_PARTITION_SEED);
4781 : ColumnsHashData *my_extra;
4782 1555 : uint64 rowHash = 0;
4783 :
4784 : /* Return false if the parent OID, modulus, or remainder is NULL. */
4785 1555 : if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2))
4786 10 : PG_RETURN_BOOL(false);
4787 1545 : parentId = PG_GETARG_OID(0);
4788 1545 : modulus = PG_GETARG_INT32(1);
4789 1545 : remainder = PG_GETARG_INT32(2);
4790 :
4791 : /* Sanity check modulus and remainder. */
4792 1545 : if (modulus <= 0)
4793 4 : ereport(ERROR,
4794 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4795 : errmsg("modulus for hash partition must be an integer value greater than zero")));
4796 1541 : if (remainder < 0)
4797 4 : ereport(ERROR,
4798 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4799 : errmsg("remainder for hash partition must be an integer value greater than or equal to zero")));
4800 1537 : if (remainder >= modulus)
4801 4 : ereport(ERROR,
4802 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4803 : errmsg("remainder for hash partition must be less than modulus")));
4804 :
4805 : /*
4806 : * Cache hash function information.
4807 : */
4808 1533 : my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
4809 1533 : if (my_extra == NULL || my_extra->relid != parentId)
4810 : {
4811 : Relation parent;
4812 : PartitionKey key;
4813 : int j;
4814 :
4815 : /* Open parent relation and fetch partition key info */
4816 531 : parent = relation_open(parentId, AccessShareLock);
4817 527 : key = RelationGetPartitionKey(parent);
4818 :
4819 : /* Reject parent table that is not hash-partitioned. */
4820 527 : if (key == NULL || key->strategy != PARTITION_STRATEGY_HASH)
4821 8 : ereport(ERROR,
4822 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4823 : errmsg("\"%s\" is not a hash partitioned table",
4824 : get_rel_name(parentId))));
4825 :
4826 519 : if (!get_fn_expr_variadic(fcinfo->flinfo))
4827 : {
4828 497 : int nargs = PG_NARGS() - 3;
4829 :
4830 : /* complain if wrong number of column values */
4831 497 : if (key->partnatts != nargs)
4832 8 : ereport(ERROR,
4833 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4834 : errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
4835 : key->partnatts, nargs)));
4836 :
4837 : /* allocate space for our cache */
4838 978 : fcinfo->flinfo->fn_extra =
4839 489 : MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
4840 : offsetof(ColumnsHashData, partsupfunc) +
4841 489 : sizeof(FmgrInfo) * nargs);
4842 489 : my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
4843 489 : my_extra->relid = parentId;
4844 489 : my_extra->nkeys = key->partnatts;
4845 489 : memcpy(my_extra->partcollid, key->partcollation,
4846 489 : key->partnatts * sizeof(Oid));
4847 :
4848 : /* check argument types and save fmgr_infos */
4849 1008 : for (j = 0; j < key->partnatts; ++j)
4850 : {
4851 523 : Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, j + 3);
4852 :
4853 523 : if (argtype != key->parttypid[j] && !IsBinaryCoercible(argtype, key->parttypid[j]))
4854 4 : ereport(ERROR,
4855 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4856 : errmsg("column %d of the partition key has type %s, but supplied value is of type %s",
4857 : j + 1, format_type_be(key->parttypid[j]), format_type_be(argtype))));
4858 :
4859 519 : fmgr_info_copy(&my_extra->partsupfunc[j],
4860 519 : &key->partsupfunc[j],
4861 519 : fcinfo->flinfo->fn_mcxt);
4862 : }
4863 : }
4864 : else
4865 : {
4866 22 : ArrayType *variadic_array = PG_GETARG_ARRAYTYPE_P(3);
4867 :
4868 : /* allocate space for our cache -- just one FmgrInfo in this case */
4869 44 : fcinfo->flinfo->fn_extra =
4870 22 : MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
4871 : offsetof(ColumnsHashData, partsupfunc) +
4872 : sizeof(FmgrInfo));
4873 22 : my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
4874 22 : my_extra->relid = parentId;
4875 22 : my_extra->nkeys = key->partnatts;
4876 22 : my_extra->variadic_type = ARR_ELEMTYPE(variadic_array);
4877 22 : get_typlenbyvalalign(my_extra->variadic_type,
4878 : &my_extra->variadic_typlen,
4879 : &my_extra->variadic_typbyval,
4880 : &my_extra->variadic_typalign);
4881 22 : my_extra->partcollid[0] = key->partcollation[0];
4882 :
4883 : /* check argument types */
4884 54 : for (j = 0; j < key->partnatts; ++j)
4885 40 : if (key->parttypid[j] != my_extra->variadic_type)
4886 8 : ereport(ERROR,
4887 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4888 : errmsg("column %d of the partition key has type \"%s\", but supplied value is of type \"%s\"",
4889 : j + 1,
4890 : format_type_be(key->parttypid[j]),
4891 : format_type_be(my_extra->variadic_type))));
4892 :
4893 14 : fmgr_info_copy(&my_extra->partsupfunc[0],
4894 : &key->partsupfunc[0],
4895 14 : fcinfo->flinfo->fn_mcxt);
4896 : }
4897 :
4898 : /* Hold lock until commit */
4899 499 : relation_close(parent, NoLock);
4900 : }
4901 :
4902 1501 : if (!OidIsValid(my_extra->variadic_type))
4903 : {
4904 1487 : int nkeys = my_extra->nkeys;
4905 : int i;
4906 :
4907 : /*
4908 : * For a non-variadic call, neither the number of arguments nor their
4909 : * types can change across calls, so avoid the expense of rechecking
4910 : * here.
4911 : */
4912 :
4913 3004 : for (i = 0; i < nkeys; i++)
4914 : {
4915 : Datum hash;
4916 :
4917 : /* keys start from fourth argument of function. */
4918 1517 : int argno = i + 3;
4919 :
4920 1517 : if (PG_ARGISNULL(argno))
4921 0 : continue;
4922 :
4923 1517 : hash = FunctionCall2Coll(&my_extra->partsupfunc[i],
4924 : my_extra->partcollid[i],
4925 : PG_GETARG_DATUM(argno),
4926 : seed);
4927 :
4928 : /* Form a single 64-bit hash value */
4929 1517 : rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
4930 : }
4931 : }
4932 : else
4933 : {
4934 14 : ArrayType *variadic_array = PG_GETARG_ARRAYTYPE_P(3);
4935 : int i;
4936 : int nelems;
4937 : Datum *datum;
4938 : bool *isnull;
4939 :
4940 14 : deconstruct_array(variadic_array,
4941 : my_extra->variadic_type,
4942 14 : my_extra->variadic_typlen,
4943 14 : my_extra->variadic_typbyval,
4944 14 : my_extra->variadic_typalign,
4945 : &datum, &isnull, &nelems);
4946 :
4947 : /* complain if wrong number of column values */
4948 14 : if (nelems != my_extra->nkeys)
4949 4 : ereport(ERROR,
4950 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4951 : errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
4952 : my_extra->nkeys, nelems)));
4953 :
4954 30 : for (i = 0; i < nelems; i++)
4955 : {
4956 : Datum hash;
4957 :
4958 20 : if (isnull[i])
4959 0 : continue;
4960 :
4961 20 : hash = FunctionCall2Coll(&my_extra->partsupfunc[0],
4962 : my_extra->partcollid[0],
4963 20 : datum[i],
4964 : seed);
4965 :
4966 : /* Form a single 64-bit hash value */
4967 20 : rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
4968 : }
4969 : }
4970 :
4971 1497 : PG_RETURN_BOOL(rowHash % modulus == remainder);
4972 : }
4973 :
4974 : /*
4975 : * check_two_partitions_bounds_range
4976 : *
4977 : * (function for BY RANGE partitioning)
4978 : *
4979 : * This is a helper function for check_partitions_for_split() and
4980 : * calculate_partition_bound_for_merge(). This function compares the upper
4981 : * bound of first_bound and the lower bound of second_bound. These bounds
4982 : * should be equal except when "defaultPart == true" (this means that one of
4983 : * the split partitions is DEFAULT). In this case, the upper bound of
4984 : * first_bound can be less than the lower bound of second_bound because
4985 : * the space between these bounds will be included in the DEFAULT partition.
4986 : *
4987 : * parent: partitioned table
4988 : * first_name: name of the first partition
4989 : * first_bound: bound of the first partition
4990 : * second_name: name of the second partition
4991 : * second_bound: bound of the second partition
4992 : * defaultPart: true if one of the new partitions is DEFAULT
4993 : * is_merge: true indicates the operation is MERGE PARTITIONS;
4994 : * false indicates the operation is SPLIT PARTITION.
4995 : * pstate: pointer to ParseState struct for determining error position
4996 : */
4997 : static void
4998 360 : check_two_partitions_bounds_range(Relation parent,
4999 : RangeVar *first_name,
5000 : PartitionBoundSpec *first_bound,
5001 : RangeVar *second_name,
5002 : PartitionBoundSpec *second_bound,
5003 : bool defaultPart,
5004 : bool is_merge,
5005 : ParseState *pstate)
5006 : {
5007 360 : PartitionKey key = RelationGetPartitionKey(parent);
5008 : PartitionRangeBound *first_upper;
5009 : PartitionRangeBound *second_lower;
5010 : int cmpval;
5011 :
5012 : Assert(key->strategy == PARTITION_STRATEGY_RANGE);
5013 :
5014 360 : first_upper = make_one_partition_rbound(key, -1, first_bound->upperdatums, false);
5015 360 : second_lower = make_one_partition_rbound(key, -1, second_bound->lowerdatums, true);
5016 :
5017 : /*
5018 : * lower1 argument of partition_rbound_cmp() is set to false for the
5019 : * correct comparison result of the lower and upper bounds.
5020 : */
5021 360 : cmpval = partition_rbound_cmp(key->partnatts,
5022 : key->partsupfunc,
5023 : key->partcollation,
5024 : second_lower->datums, second_lower->kind,
5025 : false, first_upper);
5026 360 : if ((!defaultPart && cmpval) || (defaultPart && cmpval < 0))
5027 : {
5028 28 : PartitionRangeDatum *datum = linitial(second_bound->lowerdatums);
5029 :
5030 28 : if (is_merge)
5031 8 : ereport(ERROR,
5032 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5033 : errmsg("can not merge partition \"%s\" together with partition \"%s\"",
5034 : second_name->relname, first_name->relname),
5035 : errdetail("lower bound of partition \"%s\" is not equal to the upper bound of partition \"%s\"",
5036 : second_name->relname, first_name->relname),
5037 : errhint("ALTER TABLE ... MERGE PARTITIONS requires the partition bounds to be adjacent."),
5038 : parser_errposition(pstate, datum->location));
5039 : else
5040 20 : ereport(ERROR,
5041 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5042 : errmsg("can not split to partition \"%s\" together with partition \"%s\"",
5043 : second_name->relname, first_name->relname),
5044 : errdetail("lower bound of partition \"%s\" is not equal to the upper bound of partition \"%s\"",
5045 : second_name->relname, first_name->relname),
5046 : errhint("ALTER TABLE ... SPLIT PARTITION requires the partition bounds to be adjacent."),
5047 : parser_errposition(pstate, datum->location));
5048 : }
5049 332 : }
5050 :
5051 : /*
5052 : * get_partition_bound_spec
5053 : *
5054 : * Returns the PartitionBoundSpec for the partition with the given OID partOid.
5055 : */
5056 : static PartitionBoundSpec *
5057 512 : get_partition_bound_spec(Oid partOid)
5058 : {
5059 : HeapTuple tuple;
5060 : Datum datum;
5061 : bool isnull;
5062 512 : PartitionBoundSpec *boundspec = NULL;
5063 :
5064 : /* Try fetching the tuple from the catcache, for speed. */
5065 512 : tuple = SearchSysCache1(RELOID, partOid);
5066 512 : if (!HeapTupleIsValid(tuple))
5067 0 : elog(ERROR, "cache lookup failed for relation %u", partOid);
5068 :
5069 512 : datum = SysCacheGetAttr(RELOID, tuple,
5070 : Anum_pg_class_relpartbound,
5071 : &isnull);
5072 512 : if (isnull)
5073 0 : elog(ERROR, "partition bound for relation %u is null",
5074 : partOid);
5075 :
5076 512 : boundspec = stringToNode(TextDatumGetCString(datum));
5077 :
5078 512 : if (!IsA(boundspec, PartitionBoundSpec))
5079 0 : elog(ERROR, "expected PartitionBoundSpec for relation %u",
5080 : partOid);
5081 :
5082 512 : ReleaseSysCache(tuple);
5083 512 : return boundspec;
5084 : }
5085 :
5086 : /*
5087 : * calculate_partition_bound_for_merge
5088 : *
5089 : * Calculates the bound of the merged partition "spec" by using the bounds of
5090 : * the partitions to be merged.
5091 : *
5092 : * parent: partitioned table
5093 : * partNames: names of partitions to be merged
5094 : * partOids: Oids of partitions to be merged
5095 : * spec (out): bounds specification of the merged partition
5096 : * pstate: pointer to ParseState struct to determine error position
5097 : */
5098 : void
5099 120 : calculate_partition_bound_for_merge(Relation parent,
5100 : List *partNames,
5101 : List *partOids,
5102 : PartitionBoundSpec *spec,
5103 : ParseState *pstate)
5104 : {
5105 120 : PartitionKey key = RelationGetPartitionKey(parent);
5106 : PartitionBoundSpec *bound;
5107 :
5108 : Assert(!spec->is_default);
5109 :
5110 120 : switch (key->strategy)
5111 : {
5112 116 : case PARTITION_STRATEGY_RANGE:
5113 : {
5114 : int i;
5115 : PartitionRangeBound **lower_bounds;
5116 116 : int nparts = list_length(partOids);
5117 116 : List *bounds = NIL;
5118 :
5119 116 : lower_bounds = palloc0_array(PartitionRangeBound *, nparts);
5120 :
5121 : /*
5122 : * Create an array of lower bounds and a list of
5123 : * PartitionBoundSpec.
5124 : */
5125 492 : foreach_oid(partoid, partOids)
5126 : {
5127 260 : bound = get_partition_bound_spec(partoid);
5128 260 : i = foreach_current_index(partoid);
5129 :
5130 260 : lower_bounds[i] = make_one_partition_rbound(key, i, bound->lowerdatums, true);
5131 260 : bounds = lappend(bounds, bound);
5132 : }
5133 :
5134 : /* Sort the array of lower bounds. */
5135 116 : qsort_arg(lower_bounds, nparts, sizeof(PartitionRangeBound *),
5136 : qsort_partition_rbound_cmp, key);
5137 :
5138 : /* Ranges of partitions should be adjacent. */
5139 248 : for (i = 1; i < nparts; i++)
5140 : {
5141 140 : int index = lower_bounds[i]->index;
5142 140 : int prev_index = lower_bounds[i - 1]->index;
5143 :
5144 140 : check_two_partitions_bounds_range(parent,
5145 140 : (RangeVar *) list_nth(partNames, prev_index),
5146 140 : (PartitionBoundSpec *) list_nth(bounds, prev_index),
5147 140 : (RangeVar *) list_nth(partNames, index),
5148 140 : (PartitionBoundSpec *) list_nth(bounds, index),
5149 : false,
5150 : true,
5151 : pstate);
5152 : }
5153 :
5154 : /*
5155 : * The lower bound of the first partition is the lower bound
5156 : * of the merged partition.
5157 : */
5158 108 : spec->lowerdatums =
5159 108 : ((PartitionBoundSpec *) list_nth(bounds, lower_bounds[0]->index))->lowerdatums;
5160 :
5161 : /*
5162 : * The upper bound of the last partition is the upper bound of
5163 : * the merged partition.
5164 : */
5165 108 : spec->upperdatums =
5166 108 : ((PartitionBoundSpec *) list_nth(bounds, lower_bounds[nparts - 1]->index))->upperdatums;
5167 :
5168 108 : pfree(lower_bounds);
5169 108 : list_free(bounds);
5170 108 : break;
5171 : }
5172 :
5173 4 : case PARTITION_STRATEGY_LIST:
5174 : {
5175 : /* Consolidate bounds for all partitions in the list. */
5176 20 : foreach_oid(partoid, partOids)
5177 : {
5178 12 : bound = get_partition_bound_spec(partoid);
5179 12 : spec->listdatums = list_concat(spec->listdatums, bound->listdatums);
5180 : }
5181 4 : break;
5182 : }
5183 :
5184 0 : default:
5185 0 : elog(ERROR, "unexpected partition strategy: %d",
5186 : (int) key->strategy);
5187 : }
5188 112 : }
5189 :
5190 : /*
5191 : * partitions_listdatum_intersection
5192 : *
5193 : * (function for BY LIST partitioning)
5194 : *
5195 : * Function compares lists of values for different partitions.
5196 : * Return a list that contains *one* cell that is present in both list1 and
5197 : * list2. The returned list is freshly allocated via palloc(), but the
5198 : * cells themselves point to the same objects as the cells of the
5199 : * input lists.
5200 : *
5201 : * Currently, there is no need to collect all common partition datums from the
5202 : * two lists.
5203 : */
5204 : static List *
5205 48 : partitions_listdatum_intersection(FmgrInfo *partsupfunc, Oid *partcollation,
5206 : const List *list1, const List *list2)
5207 : {
5208 48 : List *result = NIL;
5209 :
5210 48 : if (list1 == NIL || list2 == NIL)
5211 0 : return result;
5212 :
5213 208 : foreach_node(Const, val1, list1)
5214 : {
5215 128 : bool isnull1 = val1->constisnull;
5216 :
5217 616 : foreach_node(Const, val2, list2)
5218 : {
5219 376 : if (val2->constisnull)
5220 : {
5221 24 : if (isnull1)
5222 : {
5223 0 : result = lappend(result, val1);
5224 8 : return result;
5225 : }
5226 24 : continue;
5227 : }
5228 352 : else if (isnull1)
5229 0 : continue;
5230 :
5231 : /* Compare two datum values. */
5232 352 : if (DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
5233 : partcollation[0],
5234 : val1->constvalue,
5235 : val2->constvalue)) == 0)
5236 : {
5237 8 : result = lappend(result, val1);
5238 8 : return result;
5239 : }
5240 : }
5241 : }
5242 :
5243 40 : return result;
5244 : }
5245 :
5246 : /*
5247 : * check_partitions_not_overlap_list
5248 : *
5249 : * (function for BY LIST partitioning)
5250 : *
5251 : * This is a helper function for check_partitions_for_split().
5252 : * Checks that the values of the new partitions do not overlap.
5253 : *
5254 : * parent: partitioned table
5255 : * parts: array of SinglePartitionSpec structs with info about split partitions
5256 : * nparts: size of array "parts"
5257 : */
5258 : static void
5259 20 : check_partitions_not_overlap_list(Relation parent,
5260 : SinglePartitionSpec **parts,
5261 : int nparts,
5262 : ParseState *pstate)
5263 : {
5264 20 : PartitionKey key PG_USED_FOR_ASSERTS_ONLY = RelationGetPartitionKey(parent);
5265 : int i,
5266 : j;
5267 : SinglePartitionSpec *sps1,
5268 : *sps2;
5269 : List *overlap;
5270 :
5271 : Assert(key->strategy == PARTITION_STRATEGY_LIST);
5272 :
5273 56 : for (i = 0; i < nparts; i++)
5274 : {
5275 44 : sps1 = parts[i];
5276 :
5277 84 : for (j = i + 1; j < nparts; j++)
5278 : {
5279 48 : sps2 = parts[j];
5280 :
5281 48 : overlap = partitions_listdatum_intersection(&key->partsupfunc[0],
5282 : key->partcollation,
5283 48 : sps1->bound->listdatums,
5284 48 : sps2->bound->listdatums);
5285 48 : if (list_length(overlap) > 0)
5286 : {
5287 8 : Const *val = (Const *) linitial_node(Const, overlap);
5288 :
5289 8 : ereport(ERROR,
5290 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5291 : errmsg("new partition \"%s\" would overlap with another new partition \"%s\"",
5292 : sps1->name->relname, sps2->name->relname),
5293 : parser_errposition(pstate, exprLocation((Node *) val)));
5294 : }
5295 : }
5296 : }
5297 12 : }
5298 :
5299 : /*
5300 : * check_partition_bounds_for_split_range
5301 : *
5302 : * (function for BY RANGE partitioning)
5303 : *
5304 : * Checks that bounds of new partition "spec" are inside bounds of split
5305 : * partition (with Oid splitPartOid). If first=true (this means that "spec" is
5306 : * the first of the new partitions), then the lower bound of "spec" should be
5307 : * equal (or greater than or equal in case defaultPart=true) to the lower
5308 : * bound of the split partition. If last=true (this means that "spec" is the
5309 : * last of the new partitions), then the upper bound of "spec" should be
5310 : * equal (or less than or equal in case defaultPart=true) to the upper bound
5311 : * of the split partition.
5312 : *
5313 : * parent: partitioned table
5314 : * relname: name of the new partition
5315 : * spec: bounds specification of the new partition
5316 : * splitPartOid: split partition Oid
5317 : * first: true iff the new partition "spec" is the first of the
5318 : * new partitions
5319 : * last: true iff the new partition "spec" is the last of the
5320 : * new partitions
5321 : * defaultPart: true iff new partitions contain the DEFAULT partition
5322 : * pstate: pointer to ParseState struct to determine error position
5323 : */
5324 : static void
5325 304 : check_partition_bounds_for_split_range(Relation parent,
5326 : char *relname,
5327 : PartitionBoundSpec *spec,
5328 : Oid splitPartOid,
5329 : bool first,
5330 : bool last,
5331 : bool defaultPart,
5332 : ParseState *pstate)
5333 : {
5334 304 : PartitionKey key = RelationGetPartitionKey(parent);
5335 : PartitionRangeBound *lower,
5336 : *upper;
5337 : int cmpval;
5338 :
5339 : Assert(key->strategy == PARTITION_STRATEGY_RANGE);
5340 : Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
5341 :
5342 304 : lower = make_one_partition_rbound(key, -1, spec->lowerdatums, true);
5343 304 : upper = make_one_partition_rbound(key, -1, spec->upperdatums, false);
5344 :
5345 : /*
5346 : * First, check if the resulting range would be empty with the specified
5347 : * lower and upper bounds. partition_rbound_cmp cannot return zero here,
5348 : * since the lower-bound flags are different.
5349 : */
5350 304 : cmpval = partition_rbound_cmp(key->partnatts,
5351 : key->partsupfunc,
5352 : key->partcollation,
5353 : lower->datums, lower->kind,
5354 : true, upper);
5355 : Assert(cmpval != 0);
5356 304 : if (cmpval > 0)
5357 : {
5358 : /* Point to the problematic key in the lower datums list. */
5359 4 : PartitionRangeDatum *datum = list_nth(spec->lowerdatums, cmpval - 1);
5360 :
5361 4 : ereport(ERROR,
5362 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5363 : errmsg("empty range bound specified for partition \"%s\"",
5364 : relname),
5365 : errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
5366 : get_range_partbound_string(spec->lowerdatums),
5367 : get_range_partbound_string(spec->upperdatums)),
5368 : parser_errposition(pstate, exprLocation((Node *) datum)));
5369 : }
5370 :
5371 : /*
5372 : * Need to check first and last partitions (from the set of new
5373 : * partitions)
5374 : */
5375 300 : if (first || last)
5376 : {
5377 240 : PartitionBoundSpec *split_spec = get_partition_bound_spec(splitPartOid);
5378 : PartitionRangeDatum *datum;
5379 :
5380 240 : if (first)
5381 : {
5382 : PartitionRangeBound *split_lower;
5383 :
5384 128 : split_lower = make_one_partition_rbound(key, -1, split_spec->lowerdatums, true);
5385 :
5386 128 : cmpval = partition_rbound_cmp(key->partnatts,
5387 : key->partsupfunc,
5388 : key->partcollation,
5389 : lower->datums, lower->kind,
5390 : true, split_lower);
5391 128 : if (cmpval != 0)
5392 8 : datum = list_nth(spec->lowerdatums, abs(cmpval) - 1);
5393 :
5394 : /*
5395 : * The lower bound of "spec" must equal the lower bound of the
5396 : * split partition. However, if one of the new partitions is
5397 : * DEFAULT, then it is ok for the new partition's lower bound to
5398 : * be greater than that of the split partition.
5399 : */
5400 128 : if (!defaultPart)
5401 : {
5402 120 : if (cmpval != 0)
5403 8 : ereport(ERROR,
5404 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5405 : errmsg("lower bound of partition \"%s\" is not equal to lower bound of split partition \"%s\"",
5406 : relname,
5407 : get_rel_name(splitPartOid)),
5408 : errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5409 : "ALTER TABLE ... SPLIT PARTITION"),
5410 : parser_errposition(pstate, exprLocation((Node *) datum)));
5411 : }
5412 8 : else if (cmpval < 0)
5413 0 : ereport(ERROR,
5414 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5415 : errmsg("lower bound of partition \"%s\" is less than lower bound of split partition \"%s\"",
5416 : relname,
5417 : get_rel_name(splitPartOid)),
5418 : errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5419 : "ALTER TABLE ... SPLIT PARTITION"),
5420 : parser_errposition(pstate, exprLocation((Node *) datum)));
5421 : }
5422 : else
5423 : {
5424 : PartitionRangeBound *split_upper;
5425 :
5426 112 : split_upper = make_one_partition_rbound(key, -1, split_spec->upperdatums, false);
5427 :
5428 112 : cmpval = partition_rbound_cmp(key->partnatts,
5429 : key->partsupfunc,
5430 : key->partcollation,
5431 : upper->datums, upper->kind,
5432 : false, split_upper);
5433 112 : if (cmpval != 0)
5434 12 : datum = list_nth(spec->upperdatums, abs(cmpval) - 1);
5435 :
5436 : /*
5437 : * The upper bound of "spec" must equal the upper bound of the
5438 : * split partition. However, if one of the new partitions is
5439 : * DEFAULT, then it is ok for the new partition's upper bound to
5440 : * be less than that of the split partition.
5441 : */
5442 112 : if (!defaultPart)
5443 : {
5444 104 : if (cmpval != 0)
5445 8 : ereport(ERROR,
5446 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5447 : errmsg("upper bound of partition \"%s\" is not equal to upper bound of split partition \"%s\"",
5448 : relname,
5449 : get_rel_name(splitPartOid)),
5450 : errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5451 : "ALTER TABLE ... SPLIT PARTITION"),
5452 : parser_errposition(pstate, exprLocation((Node *) datum)));
5453 : }
5454 8 : else if (cmpval > 0)
5455 0 : ereport(ERROR,
5456 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5457 : errmsg("upper bound of partition \"%s\" is greater than upper bound of split partition \"%s\"",
5458 : relname,
5459 : get_rel_name(splitPartOid)),
5460 : errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5461 : "ALTER TABLE ... SPLIT PARTITION"),
5462 : parser_errposition(pstate, exprLocation((Node *) datum)));
5463 : }
5464 : }
5465 284 : }
5466 :
5467 : /*
5468 : * check_partition_bounds_for_split_list
5469 : *
5470 : * (function for BY LIST partitioning)
5471 : *
5472 : * Checks that the bounds of the new partition are inside the bounds of the
5473 : * split partition (with Oid splitPartOid).
5474 : *
5475 : * parent: partitioned table
5476 : * relname: name of the new partition
5477 : * spec: bounds specification of the new partition
5478 : * splitPartOid: split partition Oid
5479 : * pstate: pointer to ParseState struct to determine error position
5480 : */
5481 : static void
5482 72 : check_partition_bounds_for_split_list(Relation parent, char *relname,
5483 : PartitionBoundSpec *spec,
5484 : Oid splitPartOid,
5485 : ParseState *pstate)
5486 : {
5487 72 : PartitionKey key = RelationGetPartitionKey(parent);
5488 72 : PartitionDesc partdesc = RelationGetPartitionDesc(parent, false);
5489 72 : PartitionBoundInfo boundinfo = partdesc->boundinfo;
5490 72 : int with = -1;
5491 72 : bool overlap = false;
5492 72 : int overlap_location = -1;
5493 :
5494 : Assert(key->strategy == PARTITION_STRATEGY_LIST);
5495 : Assert(spec->strategy == PARTITION_STRATEGY_LIST);
5496 : Assert(boundinfo && boundinfo->strategy == PARTITION_STRATEGY_LIST);
5497 :
5498 : /*
5499 : * Search each value of the new partition "spec" in the existing
5500 : * partitions. All of them should be in the split partition (with Oid
5501 : * splitPartOid).
5502 : */
5503 344 : foreach_node(Const, val, spec->listdatums)
5504 : {
5505 220 : overlap_location = exprLocation((Node *) val);
5506 220 : if (!val->constisnull)
5507 : {
5508 : int offset;
5509 : bool equal;
5510 :
5511 212 : offset = partition_list_bsearch(&key->partsupfunc[0],
5512 : key->partcollation,
5513 : boundinfo,
5514 : val->constvalue,
5515 : &equal);
5516 212 : if (offset >= 0 && equal)
5517 : {
5518 208 : with = boundinfo->indexes[offset];
5519 208 : if (partdesc->oids[with] != splitPartOid)
5520 : {
5521 4 : overlap = true;
5522 4 : break;
5523 : }
5524 : }
5525 : else
5526 4 : ereport(ERROR,
5527 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5528 : errmsg("new partition \"%s\" cannot have this value because split partition \"%s\" does not have",
5529 : relname,
5530 : get_rel_name(splitPartOid)),
5531 : parser_errposition(pstate, overlap_location));
5532 : }
5533 8 : else if (partition_bound_accepts_nulls(boundinfo))
5534 : {
5535 4 : with = boundinfo->null_index;
5536 4 : if (partdesc->oids[with] != splitPartOid)
5537 : {
5538 0 : overlap = true;
5539 0 : break;
5540 : }
5541 : }
5542 : else
5543 4 : ereport(ERROR,
5544 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5545 : errmsg("new partition \"%s\" cannot have NULL value because split partition \"%s\" does not have",
5546 : relname,
5547 : get_rel_name(splitPartOid)),
5548 : parser_errposition(pstate, overlap_location));
5549 : }
5550 :
5551 64 : if (overlap)
5552 : {
5553 : Assert(with >= 0);
5554 4 : ereport(ERROR,
5555 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5556 : errmsg("new partition \"%s\" would overlap with another (not split) partition \"%s\"",
5557 : relname, get_rel_name(partdesc->oids[with])),
5558 : parser_errposition(pstate, overlap_location));
5559 : }
5560 60 : }
5561 :
5562 : /*
5563 : * find_value_in_new_partitions_list
5564 : *
5565 : * (function for BY LIST partitioning)
5566 : *
5567 : * Function returns true iff any of the new partitions contains the value
5568 : * "value".
5569 : *
5570 : * partsupfunc: information about the comparison function associated with
5571 : * the partition key
5572 : * partcollation: partitioning collation
5573 : * parts: pointer to an array with new partition descriptions
5574 : * nparts: number of new partitions
5575 : * value: the value that we are looking for
5576 : * isnull: true if the value that we are looking for is NULL
5577 : */
5578 : static bool
5579 60 : find_value_in_new_partitions_list(FmgrInfo *partsupfunc,
5580 : Oid *partcollation,
5581 : SinglePartitionSpec **parts,
5582 : int nparts,
5583 : Datum value,
5584 : bool isnull)
5585 : {
5586 144 : for (int i = 0; i < nparts; i++)
5587 : {
5588 136 : SinglePartitionSpec *sps = parts[i];
5589 :
5590 524 : foreach_node(Const, val, sps->bound->listdatums)
5591 : {
5592 356 : if (isnull && val->constisnull)
5593 52 : return true;
5594 :
5595 352 : if (!isnull && !val->constisnull)
5596 : {
5597 280 : if (DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
5598 : partcollation[0],
5599 : val->constvalue,
5600 : value)) == 0)
5601 48 : return true;
5602 : }
5603 : }
5604 : }
5605 8 : return false;
5606 : }
5607 :
5608 : /*
5609 : * check_parent_values_in_new_partitions
5610 : *
5611 : * (function for BY LIST partitioning)
5612 : *
5613 : * Checks that all values of split partition (with Oid partOid) are contained
5614 : * in new partitions.
5615 : *
5616 : * parent: partitioned table
5617 : * partOid: split partition Oid
5618 : * parts: pointer to an array with new partition descriptions
5619 : * nparts: number of new partitions
5620 : * pstate: pointer to ParseState struct to determine error position
5621 : */
5622 : static void
5623 12 : check_parent_values_in_new_partitions(Relation parent,
5624 : Oid partOid,
5625 : SinglePartitionSpec **parts,
5626 : int nparts,
5627 : ParseState *pstate)
5628 : {
5629 12 : PartitionKey key = RelationGetPartitionKey(parent);
5630 12 : PartitionDesc partdesc = RelationGetPartitionDesc(parent, false);
5631 12 : PartitionBoundInfo boundinfo = partdesc->boundinfo;
5632 : int i;
5633 12 : bool found = true;
5634 12 : Datum datum = PointerGetDatum(NULL);
5635 :
5636 : Assert(key->strategy == PARTITION_STRATEGY_LIST);
5637 :
5638 : /*
5639 : * Special processing for NULL value. Search for a NULL value if the split
5640 : * partition (partOid) contains it.
5641 : */
5642 12 : if (partition_bound_accepts_nulls(boundinfo) &&
5643 8 : partdesc->oids[boundinfo->null_index] == partOid)
5644 : {
5645 8 : if (!find_value_in_new_partitions_list(&key->partsupfunc[0],
5646 : key->partcollation, parts, nparts, datum, true))
5647 4 : found = false;
5648 : }
5649 :
5650 12 : if (!found)
5651 4 : ereport(ERROR,
5652 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5653 : errmsg("new partitions combined partition bounds do not contain value (%s) but split partition \"%s\" does",
5654 : "NULL",
5655 : get_rel_name(partOid)),
5656 : errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5657 : "ALTER TABLE ... SPLIT PARTITION"));
5658 :
5659 : /*
5660 : * Search all values of split partition with partOid in the PartitionDesc
5661 : * of partitioned table.
5662 : */
5663 72 : for (i = 0; i < boundinfo->ndatums; i++)
5664 : {
5665 68 : if (partdesc->oids[boundinfo->indexes[i]] == partOid)
5666 : {
5667 : /* We found the value that the split partition contains. */
5668 52 : datum = boundinfo->datums[i][0];
5669 52 : if (!find_value_in_new_partitions_list(&key->partsupfunc[0],
5670 : key->partcollation, parts, nparts, datum, false))
5671 : {
5672 4 : found = false;
5673 4 : break;
5674 : }
5675 : }
5676 : }
5677 :
5678 8 : if (!found)
5679 : {
5680 : Const *notFoundVal;
5681 :
5682 : /*
5683 : * Make a Const for getting the string representation of the missing
5684 : * value.
5685 : */
5686 4 : notFoundVal = makeConst(key->parttypid[0],
5687 4 : key->parttypmod[0],
5688 4 : key->parttypcoll[0],
5689 4 : key->parttyplen[0],
5690 : datum,
5691 : false, /* isnull */
5692 4 : key->parttypbyval[0]);
5693 :
5694 4 : ereport(ERROR,
5695 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
5696 : errmsg("new partitions combined partition bounds do not contain value (%s) but split partition \"%s\" does",
5697 : deparse_expression((Node *) notFoundVal, NIL, false, false),
5698 : get_rel_name(partOid)),
5699 : errhint("%s require combined bounds of new partitions must exactly match the bound of the split partition",
5700 : "ALTER TABLE ... SPLIT PARTITION"));
5701 : }
5702 4 : }
5703 :
5704 : /*
5705 : * check_partitions_for_split
5706 : *
5707 : * Checks new partitions for the SPLIT PARTITION command:
5708 : * 1. Bounds of new partitions should not overlap with new and existing
5709 : * partitions.
5710 : * 2. In the case when new or existing partitions contain the DEFAULT
5711 : * partition, new partitions can have any bounds inside the split partition
5712 : * bound (can be spaces between partition bounds).
5713 : * 3. In case new partitions don't contain the DEFAULT partition and the
5714 : * partitioned table does not have the DEFAULT partition, the following
5715 : * should be true: the sum of the bounds of new partitions should be equal
5716 : & to the bound of the split partition.
5717 : *
5718 : * parent: partitioned table
5719 : * splitPartOid: split partition Oid
5720 : * partlist: list of new partitions after partition split
5721 : * pstate: pointer to ParseState struct for determine error position
5722 : */
5723 : void
5724 196 : check_partitions_for_split(Relation parent,
5725 : Oid splitPartOid,
5726 : List *partlist,
5727 : ParseState *pstate)
5728 : {
5729 : PartitionKey key;
5730 : char strategy;
5731 : Oid defaultPartOid;
5732 : bool isSplitPartDefault;
5733 196 : bool createDefaultPart = false;
5734 196 : int default_index = -1;
5735 : int i;
5736 : SinglePartitionSpec **new_parts;
5737 196 : SinglePartitionSpec *spsPrev = NULL;
5738 :
5739 : /*
5740 : * nparts counts the number of split partitions, but it exclude the
5741 : * default partition.
5742 : */
5743 196 : int nparts = 0;
5744 :
5745 196 : key = RelationGetPartitionKey(parent);
5746 196 : strategy = get_partition_strategy(key);
5747 :
5748 : defaultPartOid =
5749 196 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent, true));
5750 :
5751 : Assert(strategy == PARTITION_STRATEGY_RANGE ||
5752 : strategy == PARTITION_STRATEGY_LIST);
5753 :
5754 : /*
5755 : * Make an array new_parts with new partitions except the DEFAULT
5756 : * partition.
5757 : */
5758 196 : new_parts = palloc0_array(SinglePartitionSpec *, list_length(partlist));
5759 :
5760 : /* isSplitPartDefault flag: is split partition a DEFAULT partition? */
5761 196 : isSplitPartDefault = (defaultPartOid == splitPartOid);
5762 :
5763 960 : foreach_node(SinglePartitionSpec, sps, partlist)
5764 : {
5765 568 : if (sps->bound->is_default)
5766 44 : default_index = foreach_current_index(sps);
5767 : else
5768 524 : new_parts[nparts++] = sps;
5769 : }
5770 :
5771 : /* An indicator that the DEFAULT partition will be created. */
5772 196 : if (default_index != -1)
5773 : {
5774 44 : createDefaultPart = true;
5775 : Assert(nparts == list_length(partlist) - 1);
5776 : }
5777 :
5778 196 : if (strategy == PARTITION_STRATEGY_RANGE)
5779 : {
5780 : PartitionRangeBound **lower_bounds;
5781 : SinglePartitionSpec **tmp_new_parts;
5782 :
5783 : /*
5784 : * To simplify the check for ranges of new partitions, we need to sort
5785 : * all partitions in ascending order of their bounds (we compare the
5786 : * lower bound only).
5787 : */
5788 164 : lower_bounds = palloc0_array(PartitionRangeBound *, nparts);
5789 :
5790 : /* Create an array of lower bounds. */
5791 596 : for (i = 0; i < nparts; i++)
5792 : {
5793 432 : lower_bounds[i] = make_one_partition_rbound(key, i,
5794 432 : new_parts[i]->bound->lowerdatums, true);
5795 : }
5796 :
5797 : /* Sort the array of lower bounds. */
5798 164 : qsort_arg(lower_bounds, nparts, sizeof(PartitionRangeBound *),
5799 : qsort_partition_rbound_cmp, (void *) key);
5800 :
5801 : /* Reorder the array of partitions. */
5802 164 : tmp_new_parts = new_parts;
5803 164 : new_parts = palloc0_array(SinglePartitionSpec *, nparts);
5804 596 : for (i = 0; i < nparts; i++)
5805 432 : new_parts[i] = tmp_new_parts[lower_bounds[i]->index];
5806 :
5807 164 : pfree(tmp_new_parts);
5808 164 : pfree(lower_bounds);
5809 : }
5810 :
5811 612 : for (i = 0; i < nparts; i++)
5812 : {
5813 468 : SinglePartitionSpec *sps = new_parts[i];
5814 :
5815 468 : if (isSplitPartDefault)
5816 : {
5817 : /*
5818 : * When the split partition is the DEFAULT partition, we can use
5819 : * any free ranges - as when creating a new partition.
5820 : */
5821 92 : check_new_partition_bound(sps->name->relname, parent, sps->bound,
5822 : pstate);
5823 : }
5824 : else
5825 : {
5826 : /*
5827 : * Checks that the bounds of the current partition are inside the
5828 : * bounds of the split partition. For range partitioning: checks
5829 : * that the upper bound of the previous partition is equal to the
5830 : * lower bound of the current partition. For list partitioning:
5831 : * checks that the split partition contains all values of the
5832 : * current partition.
5833 : */
5834 376 : if (strategy == PARTITION_STRATEGY_RANGE)
5835 : {
5836 304 : bool first = (i == 0);
5837 304 : bool last = (i == (nparts - 1));
5838 :
5839 304 : check_partition_bounds_for_split_range(parent, sps->name->relname, sps->bound,
5840 : splitPartOid, first, last,
5841 : createDefaultPart, pstate);
5842 : }
5843 : else
5844 72 : check_partition_bounds_for_split_list(parent, sps->name->relname,
5845 : sps->bound, splitPartOid, pstate);
5846 : }
5847 :
5848 : /* Ranges of new partitions should not overlap. */
5849 436 : if (strategy == PARTITION_STRATEGY_RANGE && spsPrev)
5850 220 : check_two_partitions_bounds_range(parent, spsPrev->name, spsPrev->bound,
5851 : sps->name, sps->bound,
5852 : createDefaultPart,
5853 : false,
5854 : pstate);
5855 :
5856 416 : spsPrev = sps;
5857 : }
5858 :
5859 144 : if (strategy == PARTITION_STRATEGY_LIST)
5860 : {
5861 : /* Values of new partitions should not overlap. */
5862 20 : check_partitions_not_overlap_list(parent, new_parts, nparts,
5863 : pstate);
5864 :
5865 : /*
5866 : * Need to check that all values of the split partition are contained
5867 : * in the new partitions. Skip this check if the DEFAULT partition
5868 : * exists.
5869 : */
5870 12 : if (!createDefaultPart)
5871 12 : check_parent_values_in_new_partitions(parent, splitPartOid,
5872 : new_parts, nparts, pstate);
5873 : }
5874 :
5875 128 : pfree(new_parts);
5876 128 : }
|