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