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