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