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