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