PostgreSQL Source Code  git master
partition.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * partition.c
4  * Partitioning related data structures and functions.
5  *
6  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  * src/backend/catalog/partition.c
12  *
13  *-------------------------------------------------------------------------
14 */
15 
16 #include "postgres.h"
17 
18 #include "access/hash.h"
19 #include "access/heapam.h"
20 #include "access/htup_details.h"
21 #include "access/nbtree.h"
22 #include "access/sysattr.h"
23 #include "catalog/dependency.h"
24 #include "catalog/indexing.h"
25 #include "catalog/objectaddress.h"
26 #include "catalog/partition.h"
27 #include "catalog/pg_collation.h"
28 #include "catalog/pg_inherits.h"
29 #include "catalog/pg_inherits_fn.h"
30 #include "catalog/pg_opclass.h"
32 #include "catalog/pg_type.h"
33 #include "commands/tablecmds.h"
34 #include "executor/executor.h"
35 #include "miscadmin.h"
36 #include "nodes/makefuncs.h"
37 #include "nodes/nodeFuncs.h"
38 #include "nodes/parsenodes.h"
39 #include "optimizer/clauses.h"
40 #include "optimizer/planmain.h"
41 #include "optimizer/prep.h"
42 #include "optimizer/var.h"
43 #include "parser/parse_coerce.h"
44 #include "rewrite/rewriteManip.h"
45 #include "storage/lmgr.h"
46 #include "utils/array.h"
47 #include "utils/builtins.h"
48 #include "utils/datum.h"
49 #include "utils/fmgroids.h"
50 #include "utils/hashutils.h"
51 #include "utils/inval.h"
52 #include "utils/lsyscache.h"
53 #include "utils/memutils.h"
54 #include "utils/rel.h"
55 #include "utils/ruleutils.h"
56 #include "utils/syscache.h"
57 
58 /*
59  * Information about bounds of a partitioned relation
60  *
61  * A list partition datum that is known to be NULL is never put into the
62  * datums array. Instead, it is tracked using the null_index field.
63  *
64  * In the case of range partitioning, ndatums will typically be far less than
65  * 2 * nparts, because a partition's upper bound and the next partition's lower
66  * bound are the same in most common cases, and we only store one of them (the
67  * upper bound). In case of hash partitioning, ndatums will be same as the
68  * number of partitions.
69  *
70  * For range and list partitioned tables, datums is an array of datum-tuples
71  * with key->partnatts datums each. For hash partitioned tables, it is an array
72  * of datum-tuples with 2 datums, modulus and remainder, corresponding to a
73  * given partition.
74  *
75  * The datums in datums array are arranged in increasing order as defined by
76  * functions qsort_partition_rbound_cmp(), qsort_partition_list_value_cmp() and
77  * qsort_partition_hbound_cmp() for range, list and hash partitioned tables
78  * respectively. For range and list partitions this simply means that the
79  * datums in the datums array are arranged in increasing order as defined by
80  * the partition key's operator classes and collations.
81  *
82  * In the case of list partitioning, the indexes array stores one entry for
83  * every datum, which is the index of the partition that accepts a given datum.
84  * In case of range partitioning, it stores one entry per distinct range
85  * datum, which is the index of the partition for which a given datum
86  * is an upper bound. In the case of hash partitioning, the number of the
87  * entries in the indexes array is same as the greatest modulus amongst all
88  * partitions. For a given partition key datum-tuple, the index of the
89  * partition which would accept that datum-tuple would be given by the entry
90  * pointed by remainder produced when hash value of the datum-tuple is divided
91  * by the greatest modulus.
92  */
93 
94 typedef struct PartitionBoundInfoData
95 {
96  char strategy; /* hash, list or range? */
97  int ndatums; /* Length of the datums following array */
99  PartitionRangeDatumKind **kind; /* The kind of each range bound datum;
100  * NULL for hash and list partitioned
101  * tables */
102  int *indexes; /* Partition indexes */
103  int null_index; /* Index of the null-accepting partition; -1
104  * if there isn't one */
105  int default_index; /* Index of the default partition; -1 if there
106  * isn't one */
108 
109 #define partition_bound_accepts_nulls(bi) ((bi)->null_index != -1)
110 #define partition_bound_has_default(bi) ((bi)->default_index != -1)
111 
112 /*
113  * When qsort'ing partition bounds after reading from the catalog, each bound
114  * is represented with one of the following structs.
115  */
116 
117 /* One bound of a hash partition */
118 typedef struct PartitionHashBound
119 {
120  int modulus;
122  int index;
124 
125 /* One value coming from some (index'th) list partition */
126 typedef struct PartitionListValue
127 {
128  int index;
131 
132 /* One bound of a range partition */
133 typedef struct PartitionRangeBound
134 {
135  int index;
136  Datum *datums; /* range bound datums */
137  PartitionRangeDatumKind *kind; /* the kind of each datum */
138  bool lower; /* this is the lower (vs upper) bound */
140 
141 static int32 qsort_partition_hbound_cmp(const void *a, const void *b);
142 static int32 qsort_partition_list_value_cmp(const void *a, const void *b,
143  void *arg);
144 static int32 qsort_partition_rbound_cmp(const void *a, const void *b,
145  void *arg);
146 
147 static Oid get_partition_operator(PartitionKey key, int col,
148  StrategyNumber strategy, bool *need_relabel);
149 static Expr *make_partition_op_expr(PartitionKey key, int keynum,
150  uint16 strategy, Expr *arg1, Expr *arg2);
151 static void get_range_key_properties(PartitionKey key, int keynum,
152  PartitionRangeDatum *ldatum,
153  PartitionRangeDatum *udatum,
154  ListCell **partexprs_item,
155  Expr **keyCol,
156  Const **lower_val, Const **upper_val);
157 static List *get_qual_for_hash(Relation parent, PartitionBoundSpec *spec);
158 static List *get_qual_for_list(Relation parent, PartitionBoundSpec *spec);
159 static List *get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
160  bool for_default);
163 
165  List *datums, bool lower);
166 static int32 partition_hbound_cmp(int modulus1, int remainder1, int modulus2,
167  int remainder2);
169  Datum *datums1, PartitionRangeDatumKind *kind1,
170  bool lower1, PartitionRangeBound *b2);
172  Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
173  Datum *tuple_datums, int n_tuple_datums);
174 
175 static int partition_list_bsearch(PartitionKey key,
176  PartitionBoundInfo boundinfo,
177  Datum value, bool *is_equal);
179  PartitionBoundInfo boundinfo,
180  PartitionRangeBound *probe, bool *is_equal);
182  PartitionBoundInfo boundinfo,
183  int nvalues, Datum *values, bool *is_equal);
184 static int partition_hash_bsearch(PartitionKey key,
185  PartitionBoundInfo boundinfo,
186  int modulus, int remainder);
187 
190 static uint64 compute_hash_value(PartitionKey key, Datum *values, bool *isnull);
191 
192 /* SQL-callable function for use in hash partition CHECK constraints */
194 
195 /*
196  * RelationBuildPartitionDesc
197  * Form rel's partition descriptor
198  *
199  * Not flushed from the cache by RelationClearRelation() unless changed because
200  * of addition or removal of partition.
201  */
202 void
204 {
205  List *inhoids,
206  *partoids;
207  Oid *oids = NULL;
208  List *boundspecs = NIL;
209  ListCell *cell;
210  int i,
211  nparts;
213  PartitionDesc result;
214  MemoryContext oldcxt;
215 
216  int ndatums = 0;
217  int default_index = -1;
218 
219  /* Hash partitioning specific */
220  PartitionHashBound **hbounds = NULL;
221 
222  /* List partitioning specific */
223  PartitionListValue **all_values = NULL;
224  int null_index = -1;
225 
226  /* Range partitioning specific */
227  PartitionRangeBound **rbounds = NULL;
228 
229  /*
230  * The following could happen in situations where rel has a pg_class entry
231  * but not the pg_partitioned_table entry yet.
232  */
233  if (key == NULL)
234  return;
235 
236  /* Get partition oids from pg_inherits */
238 
239  /* Collect bound spec nodes in a list */
240  i = 0;
241  partoids = NIL;
242  foreach(cell, inhoids)
243  {
244  Oid inhrelid = lfirst_oid(cell);
245  HeapTuple tuple;
246  Datum datum;
247  bool isnull;
248  Node *boundspec;
249 
250  tuple = SearchSysCache1(RELOID, inhrelid);
251  if (!HeapTupleIsValid(tuple))
252  elog(ERROR, "cache lookup failed for relation %u", inhrelid);
253 
254  /*
255  * It is possible that the pg_class tuple of a partition has not been
256  * updated yet to set its relpartbound field. The only case where
257  * this happens is when we open the parent relation to check using its
258  * partition descriptor that a new partition's bound does not overlap
259  * some existing partition.
260  */
261  if (!((Form_pg_class) GETSTRUCT(tuple))->relispartition)
262  {
263  ReleaseSysCache(tuple);
264  continue;
265  }
266 
267  datum = SysCacheGetAttr(RELOID, tuple,
269  &isnull);
270  Assert(!isnull);
271  boundspec = (Node *) stringToNode(TextDatumGetCString(datum));
272 
273  /*
274  * Sanity check: If the PartitionBoundSpec says this is the default
275  * partition, its OID should correspond to whatever's stored in
276  * pg_partitioned_table.partdefid; if not, the catalog is corrupt.
277  */
278  if (castNode(PartitionBoundSpec, boundspec)->is_default)
279  {
280  Oid partdefid;
281 
283  if (partdefid != inhrelid)
284  elog(ERROR, "expected partdefid %u, but got %u",
285  inhrelid, partdefid);
286  }
287 
288  boundspecs = lappend(boundspecs, boundspec);
289  partoids = lappend_oid(partoids, inhrelid);
290  ReleaseSysCache(tuple);
291  }
292 
293  nparts = list_length(partoids);
294 
295  if (nparts > 0)
296  {
297  oids = (Oid *) palloc(nparts * sizeof(Oid));
298  i = 0;
299  foreach(cell, partoids)
300  oids[i++] = lfirst_oid(cell);
301 
302  /* Convert from node to the internal representation */
303  if (key->strategy == PARTITION_STRATEGY_HASH)
304  {
305  ndatums = nparts;
306  hbounds = (PartitionHashBound **)
307  palloc(nparts * sizeof(PartitionHashBound *));
308 
309  i = 0;
310  foreach(cell, boundspecs)
311  {
313  lfirst(cell));
314 
315  if (spec->strategy != PARTITION_STRATEGY_HASH)
316  elog(ERROR, "invalid strategy in partition bound spec");
317 
318  hbounds[i] = (PartitionHashBound *)
319  palloc(sizeof(PartitionHashBound));
320 
321  hbounds[i]->modulus = spec->modulus;
322  hbounds[i]->remainder = spec->remainder;
323  hbounds[i]->index = i;
324  i++;
325  }
326 
327  /* Sort all the bounds in ascending order */
328  qsort(hbounds, nparts, sizeof(PartitionHashBound *),
330  }
331  else if (key->strategy == PARTITION_STRATEGY_LIST)
332  {
333  List *non_null_values = NIL;
334 
335  /*
336  * Create a unified list of non-null values across all partitions.
337  */
338  i = 0;
339  null_index = -1;
340  foreach(cell, boundspecs)
341  {
343  lfirst(cell));
344  ListCell *c;
345 
346  if (spec->strategy != PARTITION_STRATEGY_LIST)
347  elog(ERROR, "invalid strategy in partition bound spec");
348 
349  /*
350  * Note the index of the partition bound spec for the default
351  * partition. There's no datum to add to the list of non-null
352  * datums for this partition.
353  */
354  if (spec->is_default)
355  {
356  default_index = i;
357  i++;
358  continue;
359  }
360 
361  foreach(c, spec->listdatums)
362  {
363  Const *val = castNode(Const, lfirst(c));
364  PartitionListValue *list_value = NULL;
365 
366  if (!val->constisnull)
367  {
368  list_value = (PartitionListValue *)
369  palloc0(sizeof(PartitionListValue));
370  list_value->index = i;
371  list_value->value = val->constvalue;
372  }
373  else
374  {
375  /*
376  * Never put a null into the values array, flag
377  * instead for the code further down below where we
378  * construct the actual relcache struct.
379  */
380  if (null_index != -1)
381  elog(ERROR, "found null more than once");
382  null_index = i;
383  }
384 
385  if (list_value)
386  non_null_values = lappend(non_null_values,
387  list_value);
388  }
389 
390  i++;
391  }
392 
393  ndatums = list_length(non_null_values);
394 
395  /*
396  * Collect all list values in one array. Alongside the value, we
397  * also save the index of partition the value comes from.
398  */
399  all_values = (PartitionListValue **) palloc(ndatums *
400  sizeof(PartitionListValue *));
401  i = 0;
402  foreach(cell, non_null_values)
403  {
404  PartitionListValue *src = lfirst(cell);
405 
406  all_values[i] = (PartitionListValue *)
407  palloc(sizeof(PartitionListValue));
408  all_values[i]->value = src->value;
409  all_values[i]->index = src->index;
410  i++;
411  }
412 
413  qsort_arg(all_values, ndatums, sizeof(PartitionListValue *),
414  qsort_partition_list_value_cmp, (void *) key);
415  }
416  else if (key->strategy == PARTITION_STRATEGY_RANGE)
417  {
418  int k;
419  PartitionRangeBound **all_bounds,
420  *prev;
421 
422  all_bounds = (PartitionRangeBound **) palloc0(2 * nparts *
423  sizeof(PartitionRangeBound *));
424 
425  /*
426  * Create a unified list of range bounds across all the
427  * partitions.
428  */
429  i = ndatums = 0;
430  foreach(cell, boundspecs)
431  {
433  lfirst(cell));
435  *upper;
436 
437  if (spec->strategy != PARTITION_STRATEGY_RANGE)
438  elog(ERROR, "invalid strategy in partition bound spec");
439 
440  /*
441  * Note the index of the partition bound spec for the default
442  * partition. There's no datum to add to the allbounds array
443  * for this partition.
444  */
445  if (spec->is_default)
446  {
447  default_index = i++;
448  continue;
449  }
450 
451  lower = make_one_range_bound(key, i, spec->lowerdatums,
452  true);
453  upper = make_one_range_bound(key, i, spec->upperdatums,
454  false);
455  all_bounds[ndatums++] = lower;
456  all_bounds[ndatums++] = upper;
457  i++;
458  }
459 
460  Assert(ndatums == nparts * 2 ||
461  (default_index != -1 && ndatums == (nparts - 1) * 2));
462 
463  /* Sort all the bounds in ascending order */
464  qsort_arg(all_bounds, ndatums,
465  sizeof(PartitionRangeBound *),
467  (void *) key);
468 
469  /* Save distinct bounds from all_bounds into rbounds. */
470  rbounds = (PartitionRangeBound **)
471  palloc(ndatums * sizeof(PartitionRangeBound *));
472  k = 0;
473  prev = NULL;
474  for (i = 0; i < ndatums; i++)
475  {
476  PartitionRangeBound *cur = all_bounds[i];
477  bool is_distinct = false;
478  int j;
479 
480  /* Is the current bound distinct from the previous one? */
481  for (j = 0; j < key->partnatts; j++)
482  {
483  Datum cmpval;
484 
485  if (prev == NULL || cur->kind[j] != prev->kind[j])
486  {
487  is_distinct = true;
488  break;
489  }
490 
491  /*
492  * If the bounds are both MINVALUE or MAXVALUE, stop now
493  * and treat them as equal, since any values after this
494  * point must be ignored.
495  */
496  if (cur->kind[j] != PARTITION_RANGE_DATUM_VALUE)
497  break;
498 
499  cmpval = FunctionCall2Coll(&key->partsupfunc[j],
500  key->partcollation[j],
501  cur->datums[j],
502  prev->datums[j]);
503  if (DatumGetInt32(cmpval) != 0)
504  {
505  is_distinct = true;
506  break;
507  }
508  }
509 
510  /*
511  * Only if the bound is distinct save it into a temporary
512  * array i.e. rbounds which is later copied into boundinfo
513  * datums array.
514  */
515  if (is_distinct)
516  rbounds[k++] = all_bounds[i];
517 
518  prev = cur;
519  }
520 
521  /* Update ndatums to hold the count of distinct datums. */
522  ndatums = k;
523  }
524  else
525  elog(ERROR, "unexpected partition strategy: %d",
526  (int) key->strategy);
527  }
528 
529  /* Now build the actual relcache partition descriptor */
534  oldcxt = MemoryContextSwitchTo(rel->rd_pdcxt);
535 
536  result = (PartitionDescData *) palloc0(sizeof(PartitionDescData));
537  result->nparts = nparts;
538  if (nparts > 0)
539  {
540  PartitionBoundInfo boundinfo;
541  int *mapping;
542  int next_index = 0;
543 
544  result->oids = (Oid *) palloc0(nparts * sizeof(Oid));
545 
546  boundinfo = (PartitionBoundInfoData *)
548  boundinfo->strategy = key->strategy;
549  boundinfo->default_index = -1;
550  boundinfo->ndatums = ndatums;
551  boundinfo->null_index = -1;
552  boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *));
553 
554  /* Initialize mapping array with invalid values */
555  mapping = (int *) palloc(sizeof(int) * nparts);
556  for (i = 0; i < nparts; i++)
557  mapping[i] = -1;
558 
559  switch (key->strategy)
560  {
562  {
563  /* Modulus are stored in ascending order */
564  int greatest_modulus = hbounds[ndatums - 1]->modulus;
565 
566  boundinfo->indexes = (int *) palloc(greatest_modulus *
567  sizeof(int));
568 
569  for (i = 0; i < greatest_modulus; i++)
570  boundinfo->indexes[i] = -1;
571 
572  for (i = 0; i < nparts; i++)
573  {
574  int modulus = hbounds[i]->modulus;
575  int remainder = hbounds[i]->remainder;
576 
577  boundinfo->datums[i] = (Datum *) palloc(2 *
578  sizeof(Datum));
579  boundinfo->datums[i][0] = Int32GetDatum(modulus);
580  boundinfo->datums[i][1] = Int32GetDatum(remainder);
581 
582  while (remainder < greatest_modulus)
583  {
584  /* overlap? */
585  Assert(boundinfo->indexes[remainder] == -1);
586  boundinfo->indexes[remainder] = i;
587  remainder += modulus;
588  }
589 
590  mapping[hbounds[i]->index] = i;
591  pfree(hbounds[i]);
592  }
593  pfree(hbounds);
594  break;
595  }
596 
598  {
599  boundinfo->indexes = (int *) palloc(ndatums * sizeof(int));
600 
601  /*
602  * Copy values. Indexes of individual values are mapped
603  * to canonical values so that they match for any two list
604  * partitioned tables with same number of partitions and
605  * same lists per partition. One way to canonicalize is
606  * to assign the index in all_values[] of the smallest
607  * value of each partition, as the index of all of the
608  * partition's values.
609  */
610  for (i = 0; i < ndatums; i++)
611  {
612  boundinfo->datums[i] = (Datum *) palloc(sizeof(Datum));
613  boundinfo->datums[i][0] = datumCopy(all_values[i]->value,
614  key->parttypbyval[0],
615  key->parttyplen[0]);
616 
617  /* If the old index has no mapping, assign one */
618  if (mapping[all_values[i]->index] == -1)
619  mapping[all_values[i]->index] = next_index++;
620 
621  boundinfo->indexes[i] = mapping[all_values[i]->index];
622  }
623 
624  /*
625  * If null-accepting partition has no mapped index yet,
626  * assign one. This could happen if such partition
627  * accepts only null and hence not covered in the above
628  * loop which only handled non-null values.
629  */
630  if (null_index != -1)
631  {
632  Assert(null_index >= 0);
633  if (mapping[null_index] == -1)
634  mapping[null_index] = next_index++;
635  boundinfo->null_index = mapping[null_index];
636  }
637 
638  /* Assign mapped index for the default partition. */
639  if (default_index != -1)
640  {
641  /*
642  * The default partition accepts any value not
643  * specified in the lists of other partitions, hence
644  * it should not get mapped index while assigning
645  * those for non-null datums.
646  */
647  Assert(default_index >= 0 &&
648  mapping[default_index] == -1);
649  mapping[default_index] = next_index++;
650  boundinfo->default_index = mapping[default_index];
651  }
652 
653  /* All partition must now have a valid mapping */
654  Assert(next_index == nparts);
655  break;
656  }
657 
659  {
660  boundinfo->kind = (PartitionRangeDatumKind **)
661  palloc(ndatums *
662  sizeof(PartitionRangeDatumKind *));
663  boundinfo->indexes = (int *) palloc((ndatums + 1) *
664  sizeof(int));
665 
666  for (i = 0; i < ndatums; i++)
667  {
668  int j;
669 
670  boundinfo->datums[i] = (Datum *) palloc(key->partnatts *
671  sizeof(Datum));
672  boundinfo->kind[i] = (PartitionRangeDatumKind *)
673  palloc(key->partnatts *
674  sizeof(PartitionRangeDatumKind));
675  for (j = 0; j < key->partnatts; j++)
676  {
677  if (rbounds[i]->kind[j] == PARTITION_RANGE_DATUM_VALUE)
678  boundinfo->datums[i][j] =
679  datumCopy(rbounds[i]->datums[j],
680  key->parttypbyval[j],
681  key->parttyplen[j]);
682  boundinfo->kind[i][j] = rbounds[i]->kind[j];
683  }
684 
685  /*
686  * There is no mapping for invalid indexes.
687  *
688  * Any lower bounds in the rbounds array have invalid
689  * indexes assigned, because the values between the
690  * previous bound (if there is one) and this (lower)
691  * bound are not part of the range of any existing
692  * partition.
693  */
694  if (rbounds[i]->lower)
695  boundinfo->indexes[i] = -1;
696  else
697  {
698  int orig_index = rbounds[i]->index;
699 
700  /* If the old index has no mapping, assign one */
701  if (mapping[orig_index] == -1)
702  mapping[orig_index] = next_index++;
703 
704  boundinfo->indexes[i] = mapping[orig_index];
705  }
706  }
707 
708  /* Assign mapped index for the default partition. */
709  if (default_index != -1)
710  {
711  Assert(default_index >= 0 && mapping[default_index] == -1);
712  mapping[default_index] = next_index++;
713  boundinfo->default_index = mapping[default_index];
714  }
715  boundinfo->indexes[i] = -1;
716  break;
717  }
718 
719  default:
720  elog(ERROR, "unexpected partition strategy: %d",
721  (int) key->strategy);
722  }
723 
724  result->boundinfo = boundinfo;
725 
726  /*
727  * Now assign OIDs from the original array into mapped indexes of the
728  * result array. Order of OIDs in the former is defined by the
729  * catalog scan that retrieved them, whereas that in the latter is
730  * defined by canonicalized representation of the partition bounds.
731  */
732  for (i = 0; i < nparts; i++)
733  result->oids[mapping[i]] = oids[i];
734  pfree(mapping);
735  }
736 
737  MemoryContextSwitchTo(oldcxt);
738  rel->rd_partdesc = result;
739 }
740 
741 /*
742  * Are two partition bound collections logically equal?
743  *
744  * Used in the keep logic of relcache.c (ie, in RelationClearRelation()).
745  * This is also useful when b1 and b2 are bound collections of two separate
746  * relations, respectively, because PartitionBoundInfo is a canonical
747  * representation of partition bounds.
748  */
749 bool
750 partition_bounds_equal(int partnatts, int16 *parttyplen, bool *parttypbyval,
752 {
753  int i;
754 
755  if (b1->strategy != b2->strategy)
756  return false;
757 
758  if (b1->ndatums != b2->ndatums)
759  return false;
760 
761  if (b1->null_index != b2->null_index)
762  return false;
763 
764  if (b1->default_index != b2->default_index)
765  return false;
766 
768  {
769  int greatest_modulus = get_greatest_modulus(b1);
770 
771  /*
772  * If two hash partitioned tables have different greatest moduli,
773  * their partition schemes don't match.
774  */
775  if (greatest_modulus != get_greatest_modulus(b2))
776  return false;
777 
778  /*
779  * We arrange the partitions in the ascending order of their modulus
780  * and remainders. Also every modulus is factor of next larger
781  * modulus. Therefore we can safely store index of a given partition
782  * in indexes array at remainder of that partition. Also entries at
783  * (remainder + N * modulus) positions in indexes array are all same
784  * for (modulus, remainder) specification for any partition. Thus
785  * datums array from both the given bounds are same, if and only if
786  * their indexes array will be same. So, it suffices to compare
787  * indexes array.
788  */
789  for (i = 0; i < greatest_modulus; i++)
790  if (b1->indexes[i] != b2->indexes[i])
791  return false;
792 
793 #ifdef USE_ASSERT_CHECKING
794 
795  /*
796  * Nonetheless make sure that the bounds are indeed same when the
797  * indexes match. Hash partition bound stores modulus and remainder
798  * at b1->datums[i][0] and b1->datums[i][1] position respectively.
799  */
800  for (i = 0; i < b1->ndatums; i++)
801  Assert((b1->datums[i][0] == b2->datums[i][0] &&
802  b1->datums[i][1] == b2->datums[i][1]));
803 #endif
804  }
805  else
806  {
807  for (i = 0; i < b1->ndatums; i++)
808  {
809  int j;
810 
811  for (j = 0; j < partnatts; j++)
812  {
813  /* For range partitions, the bounds might not be finite. */
814  if (b1->kind != NULL)
815  {
816  /* The different kinds of bound all differ from each other */
817  if (b1->kind[i][j] != b2->kind[i][j])
818  return false;
819 
820  /*
821  * Non-finite bounds are equal without further
822  * examination.
823  */
824  if (b1->kind[i][j] != PARTITION_RANGE_DATUM_VALUE)
825  continue;
826  }
827 
828  /*
829  * Compare the actual values. Note that it would be both
830  * incorrect and unsafe to invoke the comparison operator
831  * derived from the partitioning specification here. It would
832  * be incorrect because we want the relcache entry to be
833  * updated for ANY change to the partition bounds, not just
834  * those that the partitioning operator thinks are
835  * significant. It would be unsafe because we might reach
836  * this code in the context of an aborted transaction, and an
837  * arbitrary partitioning operator might not be safe in that
838  * context. datumIsEqual() should be simple enough to be
839  * safe.
840  */
841  if (!datumIsEqual(b1->datums[i][j], b2->datums[i][j],
842  parttypbyval[j], parttyplen[j]))
843  return false;
844  }
845 
846  if (b1->indexes[i] != b2->indexes[i])
847  return false;
848  }
849 
850  /* There are ndatums+1 indexes in case of range partitions */
851  if (b1->strategy == PARTITION_STRATEGY_RANGE &&
852  b1->indexes[i] != b2->indexes[i])
853  return false;
854  }
855  return true;
856 }
857 
858 /*
859  * Return a copy of given PartitionBoundInfo structure. The data types of bounds
860  * are described by given partition key specification.
861  */
862 extern PartitionBoundInfo
864  PartitionKey key)
865 {
867  int i;
868  int ndatums;
869  int partnatts;
870  int num_indexes;
871 
873 
874  dest->strategy = src->strategy;
875  ndatums = dest->ndatums = src->ndatums;
876  partnatts = key->partnatts;
877 
878  num_indexes = get_partition_bound_num_indexes(src);
879 
880  /* List partitioned tables have only a single partition key. */
881  Assert(key->strategy != PARTITION_STRATEGY_LIST || partnatts == 1);
882 
883  dest->datums = (Datum **) palloc(sizeof(Datum *) * ndatums);
884 
885  if (src->kind != NULL)
886  {
887  dest->kind = (PartitionRangeDatumKind **) palloc(ndatums *
888  sizeof(PartitionRangeDatumKind *));
889  for (i = 0; i < ndatums; i++)
890  {
891  dest->kind[i] = (PartitionRangeDatumKind *) palloc(partnatts *
892  sizeof(PartitionRangeDatumKind));
893 
894  memcpy(dest->kind[i], src->kind[i],
895  sizeof(PartitionRangeDatumKind) * key->partnatts);
896  }
897  }
898  else
899  dest->kind = NULL;
900 
901  for (i = 0; i < ndatums; i++)
902  {
903  int j;
904 
905  /*
906  * For a corresponding to hash partition, datums array will have two
907  * elements - modulus and remainder.
908  */
909  bool hash_part = (key->strategy == PARTITION_STRATEGY_HASH);
910  int natts = hash_part ? 2 : partnatts;
911 
912  dest->datums[i] = (Datum *) palloc(sizeof(Datum) * natts);
913 
914  for (j = 0; j < natts; j++)
915  {
916  bool byval;
917  int typlen;
918 
919  if (hash_part)
920  {
921  typlen = sizeof(int32); /* Always int4 */
922  byval = true; /* int4 is pass-by-value */
923  }
924  else
925  {
926  byval = key->parttypbyval[j];
927  typlen = key->parttyplen[j];
928  }
929 
930  if (dest->kind == NULL ||
931  dest->kind[i][j] == PARTITION_RANGE_DATUM_VALUE)
932  dest->datums[i][j] = datumCopy(src->datums[i][j],
933  byval, typlen);
934  }
935  }
936 
937  dest->indexes = (int *) palloc(sizeof(int) * num_indexes);
938  memcpy(dest->indexes, src->indexes, sizeof(int) * num_indexes);
939 
940  dest->null_index = src->null_index;
941  dest->default_index = src->default_index;
942 
943  return dest;
944 }
945 
946 /*
947  * check_new_partition_bound
948  *
949  * Checks if the new partition's bound overlaps any of the existing partitions
950  * of parent. Also performs additional checks as necessary per strategy.
951  */
952 void
953 check_new_partition_bound(char *relname, Relation parent,
954  PartitionBoundSpec *spec)
955 {
957  PartitionDesc partdesc = RelationGetPartitionDesc(parent);
958  PartitionBoundInfo boundinfo = partdesc->boundinfo;
959  ParseState *pstate = make_parsestate(NULL);
960  int with = -1;
961  bool overlap = false;
962 
963  if (spec->is_default)
964  {
965  if (boundinfo == NULL || !partition_bound_has_default(boundinfo))
966  return;
967 
968  /* Default partition already exists, error out. */
969  ereport(ERROR,
970  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
971  errmsg("partition \"%s\" conflicts with existing default partition \"%s\"",
972  relname, get_rel_name(partdesc->oids[boundinfo->default_index])),
973  parser_errposition(pstate, spec->location)));
974  }
975 
976  switch (key->strategy)
977  {
979  {
981  Assert(spec->remainder >= 0 && spec->remainder < spec->modulus);
982 
983  if (partdesc->nparts > 0)
984  {
985  PartitionBoundInfo boundinfo = partdesc->boundinfo;
986  Datum **datums = boundinfo->datums;
987  int ndatums = boundinfo->ndatums;
988  int greatest_modulus;
989  int remainder;
990  int offset;
991  bool valid_modulus = true;
992  int prev_modulus, /* Previous largest modulus */
993  next_modulus; /* Next largest modulus */
994 
995  /*
996  * Check rule that every modulus must be a factor of the
997  * next larger modulus. For example, if you have a bunch
998  * of partitions that all have modulus 5, you can add a
999  * new partition with modulus 10 or a new partition with
1000  * modulus 15, but you cannot add both a partition with
1001  * modulus 10 and a partition with modulus 15, because 10
1002  * is not a factor of 15.
1003  *
1004  * Get the greatest (modulus, remainder) pair contained in
1005  * boundinfo->datums that is less than or equal to the
1006  * (spec->modulus, spec->remainder) pair.
1007  */
1008  offset = partition_hash_bsearch(key, boundinfo,
1009  spec->modulus,
1010  spec->remainder);
1011  if (offset < 0)
1012  {
1013  next_modulus = DatumGetInt32(datums[0][0]);
1014  valid_modulus = (next_modulus % spec->modulus) == 0;
1015  }
1016  else
1017  {
1018  prev_modulus = DatumGetInt32(datums[offset][0]);
1019  valid_modulus = (spec->modulus % prev_modulus) == 0;
1020 
1021  if (valid_modulus && (offset + 1) < ndatums)
1022  {
1023  next_modulus = DatumGetInt32(datums[offset + 1][0]);
1024  valid_modulus = (next_modulus % spec->modulus) == 0;
1025  }
1026  }
1027 
1028  if (!valid_modulus)
1029  ereport(ERROR,
1030  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1031  errmsg("every hash partition modulus must be a factor of the next larger modulus")));
1032 
1033  greatest_modulus = get_greatest_modulus(boundinfo);
1034  remainder = spec->remainder;
1035 
1036  /*
1037  * Normally, the lowest remainder that could conflict with
1038  * the new partition is equal to the remainder specified
1039  * for the new partition, but when the new partition has a
1040  * modulus higher than any used so far, we need to adjust.
1041  */
1042  if (remainder >= greatest_modulus)
1043  remainder = remainder % greatest_modulus;
1044 
1045  /* Check every potentially-conflicting remainder. */
1046  do
1047  {
1048  if (boundinfo->indexes[remainder] != -1)
1049  {
1050  overlap = true;
1051  with = boundinfo->indexes[remainder];
1052  break;
1053  }
1054  remainder += spec->modulus;
1055  } while (remainder < greatest_modulus);
1056  }
1057 
1058  break;
1059  }
1060 
1062  {
1064 
1065  if (partdesc->nparts > 0)
1066  {
1067  ListCell *cell;
1068 
1069  Assert(boundinfo &&
1070  boundinfo->strategy == PARTITION_STRATEGY_LIST &&
1071  (boundinfo->ndatums > 0 ||
1072  partition_bound_accepts_nulls(boundinfo) ||
1073  partition_bound_has_default(boundinfo)));
1074 
1075  foreach(cell, spec->listdatums)
1076  {
1077  Const *val = castNode(Const, lfirst(cell));
1078 
1079  if (!val->constisnull)
1080  {
1081  int offset;
1082  bool equal;
1083 
1084  offset = partition_list_bsearch(key, boundinfo,
1085  val->constvalue,
1086  &equal);
1087  if (offset >= 0 && equal)
1088  {
1089  overlap = true;
1090  with = boundinfo->indexes[offset];
1091  break;
1092  }
1093  }
1094  else if (partition_bound_accepts_nulls(boundinfo))
1095  {
1096  overlap = true;
1097  with = boundinfo->null_index;
1098  break;
1099  }
1100  }
1101  }
1102 
1103  break;
1104  }
1105 
1107  {
1109  *upper;
1110 
1112  lower = make_one_range_bound(key, -1, spec->lowerdatums, true);
1113  upper = make_one_range_bound(key, -1, spec->upperdatums, false);
1114 
1115  /*
1116  * First check if the resulting range would be empty with
1117  * specified lower and upper bounds
1118  */
1119  if (partition_rbound_cmp(key, lower->datums, lower->kind, true,
1120  upper) >= 0)
1121  {
1122  ereport(ERROR,
1123  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1124  errmsg("empty range bound specified for partition \"%s\"",
1125  relname),
1126  errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
1129  parser_errposition(pstate, spec->location)));
1130  }
1131 
1132  if (partdesc->nparts > 0)
1133  {
1134  PartitionBoundInfo boundinfo = partdesc->boundinfo;
1135  int offset;
1136  bool equal;
1137 
1138  Assert(boundinfo &&
1139  boundinfo->strategy == PARTITION_STRATEGY_RANGE &&
1140  (boundinfo->ndatums > 0 ||
1141  partition_bound_has_default(boundinfo)));
1142 
1143  /*
1144  * Test whether the new lower bound (which is treated
1145  * inclusively as part of the new partition) lies inside
1146  * an existing partition, or in a gap.
1147  *
1148  * If it's inside an existing partition, the bound at
1149  * offset + 1 will be the upper bound of that partition,
1150  * and its index will be >= 0.
1151  *
1152  * If it's in a gap, the bound at offset + 1 will be the
1153  * lower bound of the next partition, and its index will
1154  * be -1. This is also true if there is no next partition,
1155  * since the index array is initialised with an extra -1
1156  * at the end.
1157  */
1158  offset = partition_range_bsearch(key, boundinfo, lower,
1159  &equal);
1160 
1161  if (boundinfo->indexes[offset + 1] < 0)
1162  {
1163  /*
1164  * Check that the new partition will fit in the gap.
1165  * For it to fit, the new upper bound must be less
1166  * than or equal to the lower bound of the next
1167  * partition, if there is one.
1168  */
1169  if (offset + 1 < boundinfo->ndatums)
1170  {
1171  int32 cmpval;
1172  Datum *datums;
1174  bool is_lower;
1175 
1176  datums = boundinfo->datums[offset + 1];
1177  kind = boundinfo->kind[offset + 1];
1178  is_lower = (boundinfo->indexes[offset + 1] == -1);
1179 
1180  cmpval = partition_rbound_cmp(key, datums, kind,
1181  is_lower, upper);
1182  if (cmpval < 0)
1183  {
1184  /*
1185  * The new partition overlaps with the
1186  * existing partition between offset + 1 and
1187  * offset + 2.
1188  */
1189  overlap = true;
1190  with = boundinfo->indexes[offset + 2];
1191  }
1192  }
1193  }
1194  else
1195  {
1196  /*
1197  * The new partition overlaps with the existing
1198  * partition between offset and offset + 1.
1199  */
1200  overlap = true;
1201  with = boundinfo->indexes[offset + 1];
1202  }
1203  }
1204 
1205  break;
1206  }
1207 
1208  default:
1209  elog(ERROR, "unexpected partition strategy: %d",
1210  (int) key->strategy);
1211  }
1212 
1213  if (overlap)
1214  {
1215  Assert(with >= 0);
1216  ereport(ERROR,
1217  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1218  errmsg("partition \"%s\" would overlap partition \"%s\"",
1219  relname, get_rel_name(partdesc->oids[with])),
1220  parser_errposition(pstate, spec->location)));
1221  }
1222 }
1223 
1224 /*
1225  * check_default_allows_bound
1226  *
1227  * This function checks if there exists a row in the default partition that
1228  * would properly belong to the new partition being added. If it finds one,
1229  * it throws an error.
1230  */
1231 void
1233  PartitionBoundSpec *new_spec)
1234 {
1235  List *new_part_constraints;
1236  List *def_part_constraints;
1237  List *all_parts;
1238  ListCell *lc;
1239 
1240  new_part_constraints = (new_spec->strategy == PARTITION_STRATEGY_LIST)
1241  ? get_qual_for_list(parent, new_spec)
1242  : get_qual_for_range(parent, new_spec, false);
1243  def_part_constraints =
1244  get_proposed_default_constraint(new_part_constraints);
1245 
1246  /*
1247  * If the existing constraints on the default partition imply that it will
1248  * not contain any row that would belong to the new partition, we can
1249  * avoid scanning the default partition.
1250  */
1251  if (PartConstraintImpliedByRelConstraint(default_rel, def_part_constraints))
1252  {
1253  ereport(INFO,
1254  (errmsg("updated partition constraint for default partition \"%s\" is implied by existing constraints",
1255  RelationGetRelationName(default_rel))));
1256  return;
1257  }
1258 
1259  /*
1260  * Scan the default partition and its subpartitions, and check for rows
1261  * that do not satisfy the revised partition constraints.
1262  */
1263  if (default_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1264  all_parts = find_all_inheritors(RelationGetRelid(default_rel),
1265  AccessExclusiveLock, NULL);
1266  else
1267  all_parts = list_make1_oid(RelationGetRelid(default_rel));
1268 
1269  foreach(lc, all_parts)
1270  {
1271  Oid part_relid = lfirst_oid(lc);
1272  Relation part_rel;
1273  Expr *constr;
1274  Expr *partition_constraint;
1275  EState *estate;
1276  HeapTuple tuple;
1277  ExprState *partqualstate = NULL;
1278  Snapshot snapshot;
1279  TupleDesc tupdesc;
1280  ExprContext *econtext;
1281  HeapScanDesc scan;
1282  MemoryContext oldCxt;
1283  TupleTableSlot *tupslot;
1284 
1285  /* Lock already taken above. */
1286  if (part_relid != RelationGetRelid(default_rel))
1287  {
1288  part_rel = heap_open(part_relid, NoLock);
1289 
1290  /*
1291  * If the partition constraints on default partition child imply
1292  * that it will not contain any row that would belong to the new
1293  * partition, we can avoid scanning the child table.
1294  */
1296  def_part_constraints))
1297  {
1298  ereport(INFO,
1299  (errmsg("updated partition constraint for default partition \"%s\" is implied by existing constraints",
1300  RelationGetRelationName(part_rel))));
1301 
1302  heap_close(part_rel, NoLock);
1303  continue;
1304  }
1305  }
1306  else
1307  part_rel = default_rel;
1308 
1309  /*
1310  * Only RELKIND_RELATION relations (i.e. leaf partitions) need to be
1311  * scanned.
1312  */
1313  if (part_rel->rd_rel->relkind != RELKIND_RELATION)
1314  {
1315  if (part_rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1316  ereport(WARNING,
1317  (errcode(ERRCODE_CHECK_VIOLATION),
1318  errmsg("skipped scanning foreign table \"%s\" which is a partition of default partition \"%s\"",
1319  RelationGetRelationName(part_rel),
1320  RelationGetRelationName(default_rel))));
1321 
1322  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
1323  heap_close(part_rel, NoLock);
1324 
1325  continue;
1326  }
1327 
1328  tupdesc = CreateTupleDescCopy(RelationGetDescr(part_rel));
1329  constr = linitial(def_part_constraints);
1330  partition_constraint = (Expr *)
1331  map_partition_varattnos((List *) constr,
1332  1, part_rel, parent, NULL);
1333  estate = CreateExecutorState();
1334 
1335  /* Build expression execution states for partition check quals */
1336  partqualstate = ExecPrepareExpr(partition_constraint, estate);
1337 
1338  econtext = GetPerTupleExprContext(estate);
1339  snapshot = RegisterSnapshot(GetLatestSnapshot());
1340  scan = heap_beginscan(part_rel, snapshot, 0, NULL);
1341  tupslot = MakeSingleTupleTableSlot(tupdesc);
1342 
1343  /*
1344  * Switch to per-tuple memory context and reset it for each tuple
1345  * produced, so we don't leak memory.
1346  */
1348 
1349  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1350  {
1351  ExecStoreTuple(tuple, tupslot, InvalidBuffer, false);
1352  econtext->ecxt_scantuple = tupslot;
1353 
1354  if (!ExecCheck(partqualstate, econtext))
1355  ereport(ERROR,
1356  (errcode(ERRCODE_CHECK_VIOLATION),
1357  errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
1358  RelationGetRelationName(default_rel))));
1359 
1360  ResetExprContext(econtext);
1362  }
1363 
1364  MemoryContextSwitchTo(oldCxt);
1365  heap_endscan(scan);
1366  UnregisterSnapshot(snapshot);
1368  FreeExecutorState(estate);
1369 
1370  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
1371  heap_close(part_rel, NoLock); /* keep the lock until commit */
1372  }
1373 }
1374 
1375 /*
1376  * get_partition_parent
1377  *
1378  * Returns inheritance parent of a partition by scanning pg_inherits
1379  *
1380  * Note: Because this function assumes that the relation whose OID is passed
1381  * as an argument will have precisely one parent, it should only be called
1382  * when it is known that the relation is a partition.
1383  */
1384 Oid
1386 {
1387  Form_pg_inherits form;
1388  Relation catalogRelation;
1389  SysScanDesc scan;
1390  ScanKeyData key[2];
1391  HeapTuple tuple;
1392  Oid result;
1393 
1394  catalogRelation = heap_open(InheritsRelationId, AccessShareLock);
1395 
1396  ScanKeyInit(&key[0],
1398  BTEqualStrategyNumber, F_OIDEQ,
1399  ObjectIdGetDatum(relid));
1400  ScanKeyInit(&key[1],
1402  BTEqualStrategyNumber, F_INT4EQ,
1403  Int32GetDatum(1));
1404 
1405  scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId, true,
1406  NULL, 2, key);
1407 
1408  tuple = systable_getnext(scan);
1409  if (!HeapTupleIsValid(tuple))
1410  elog(ERROR, "could not find tuple for parent of relation %u", relid);
1411 
1412  form = (Form_pg_inherits) GETSTRUCT(tuple);
1413  result = form->inhparent;
1414 
1415  systable_endscan(scan);
1416  heap_close(catalogRelation, AccessShareLock);
1417 
1418  return result;
1419 }
1420 
1421 /*
1422  * get_qual_from_partbound
1423  * Given a parser node for partition bound, return the list of executable
1424  * expressions as partition constraint
1425  */
1426 List *
1428  PartitionBoundSpec *spec)
1429 {
1430  PartitionKey key = RelationGetPartitionKey(parent);
1431  List *my_qual = NIL;
1432 
1433  Assert(key != NULL);
1434 
1435  switch (key->strategy)
1436  {
1439  my_qual = get_qual_for_hash(parent, spec);
1440  break;
1441 
1444  my_qual = get_qual_for_list(parent, spec);
1445  break;
1446 
1449  my_qual = get_qual_for_range(parent, spec, false);
1450  break;
1451 
1452  default:
1453  elog(ERROR, "unexpected partition strategy: %d",
1454  (int) key->strategy);
1455  }
1456 
1457  return my_qual;
1458 }
1459 
1460 /*
1461  * map_partition_varattnos - maps varattno of any Vars in expr from the
1462  * attno's of 'from_rel' to the attno's of 'to_rel' partition, each of which
1463  * may be either a leaf partition or a partitioned table, but both of which
1464  * must be from the same partitioning hierarchy.
1465  *
1466  * Even though all of the same column names must be present in all relations
1467  * in the hierarchy, and they must also have the same types, the attnos may
1468  * be different.
1469  *
1470  * If found_whole_row is not NULL, *found_whole_row returns whether a
1471  * whole-row variable was found in the input expression.
1472  *
1473  * Note: this will work on any node tree, so really the argument and result
1474  * should be declared "Node *". But a substantial majority of the callers
1475  * are working on Lists, so it's less messy to do the casts internally.
1476  */
1477 List *
1478 map_partition_varattnos(List *expr, int fromrel_varno,
1479  Relation to_rel, Relation from_rel,
1480  bool *found_whole_row)
1481 {
1482  bool my_found_whole_row = false;
1483 
1484  if (expr != NIL)
1485  {
1486  AttrNumber *part_attnos;
1487 
1488  part_attnos = convert_tuples_by_name_map(RelationGetDescr(to_rel),
1489  RelationGetDescr(from_rel),
1490  gettext_noop("could not convert row type"));
1491  expr = (List *) map_variable_attnos((Node *) expr,
1492  fromrel_varno, 0,
1493  part_attnos,
1494  RelationGetDescr(from_rel)->natts,
1495  RelationGetForm(to_rel)->reltype,
1496  &my_found_whole_row);
1497  }
1498 
1499  if (found_whole_row)
1500  *found_whole_row = my_found_whole_row;
1501 
1502  return expr;
1503 }
1504 
1505 /*
1506  * RelationGetPartitionQual
1507  *
1508  * Returns a list of partition quals
1509  */
1510 List *
1512 {
1513  /* Quick exit */
1514  if (!rel->rd_rel->relispartition)
1515  return NIL;
1516 
1517  return generate_partition_qual(rel);
1518 }
1519 
1520 /*
1521  * get_partition_qual_relid
1522  *
1523  * Returns an expression tree describing the passed-in relation's partition
1524  * constraint. If there is no partition constraint returns NULL; this can
1525  * happen if the default partition is the only partition.
1526  */
1527 Expr *
1529 {
1530  Relation rel = heap_open(relid, AccessShareLock);
1531  Expr *result = NULL;
1532  List *and_args;
1533 
1534  /* Do the work only if this relation is a partition. */
1535  if (rel->rd_rel->relispartition)
1536  {
1537  and_args = generate_partition_qual(rel);
1538 
1539  if (and_args == NIL)
1540  result = NULL;
1541  else if (list_length(and_args) > 1)
1542  result = makeBoolExpr(AND_EXPR, and_args, -1);
1543  else
1544  result = linitial(and_args);
1545  }
1546 
1547  /* Keep the lock. */
1548  heap_close(rel, NoLock);
1549 
1550  return result;
1551 }
1552 
1553 /* Module-local functions */
1554 
1555 /*
1556  * get_partition_operator
1557  *
1558  * Return oid of the operator of given strategy for a given partition key
1559  * column.
1560  */
1561 static Oid
1563  bool *need_relabel)
1564 {
1565  Oid operoid;
1566 
1567  /*
1568  * First check if there exists an operator of the given strategy, with
1569  * this column's type as both its lefttype and righttype, in the
1570  * partitioning operator family specified for the column.
1571  */
1572  operoid = get_opfamily_member(key->partopfamily[col],
1573  key->parttypid[col],
1574  key->parttypid[col],
1575  strategy);
1576 
1577  /*
1578  * If one doesn't exist, we must resort to using an operator in the same
1579  * operator family but with the operator class declared input type. It is
1580  * OK to do so, because the column's type is known to be binary-coercible
1581  * with the operator class input type (otherwise, the operator class in
1582  * question would not have been accepted as the partitioning operator
1583  * class). We must however inform the caller to wrap the non-Const
1584  * expression with a RelabelType node to denote the implicit coercion. It
1585  * ensures that the resulting expression structurally matches similarly
1586  * processed expressions within the optimizer.
1587  */
1588  if (!OidIsValid(operoid))
1589  {
1590  operoid = get_opfamily_member(key->partopfamily[col],
1591  key->partopcintype[col],
1592  key->partopcintype[col],
1593  strategy);
1594  if (!OidIsValid(operoid))
1595  elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
1596  strategy, key->partopcintype[col], key->partopcintype[col],
1597  key->partopfamily[col]);
1598  *need_relabel = true;
1599  }
1600  else
1601  *need_relabel = false;
1602 
1603  return operoid;
1604 }
1605 
1606 /*
1607  * make_partition_op_expr
1608  * Returns an Expr for the given partition key column with arg1 and
1609  * arg2 as its leftop and rightop, respectively
1610  */
1611 static Expr *
1613  uint16 strategy, Expr *arg1, Expr *arg2)
1614 {
1615  Oid operoid;
1616  bool need_relabel = false;
1617  Expr *result = NULL;
1618 
1619  /* Get the correct btree operator for this partitioning column */
1620  operoid = get_partition_operator(key, keynum, strategy, &need_relabel);
1621 
1622  /*
1623  * Chosen operator may be such that the non-Const operand needs to be
1624  * coerced, so apply the same; see the comment in
1625  * get_partition_operator().
1626  */
1627  if (!IsA(arg1, Const) &&
1628  (need_relabel ||
1629  key->partcollation[keynum] != key->parttypcoll[keynum]))
1630  arg1 = (Expr *) makeRelabelType(arg1,
1631  key->partopcintype[keynum],
1632  -1,
1633  key->partcollation[keynum],
1635 
1636  /* Generate the actual expression */
1637  switch (key->strategy)
1638  {
1640  {
1641  List *elems = (List *) arg2;
1642  int nelems = list_length(elems);
1643 
1644  Assert(nelems >= 1);
1645  Assert(keynum == 0);
1646 
1647  if (nelems > 1 &&
1648  !type_is_array(key->parttypid[keynum]))
1649  {
1650  ArrayExpr *arrexpr;
1651  ScalarArrayOpExpr *saopexpr;
1652 
1653  /* Construct an ArrayExpr for the right-hand inputs */
1654  arrexpr = makeNode(ArrayExpr);
1655  arrexpr->array_typeid =
1656  get_array_type(key->parttypid[keynum]);
1657  arrexpr->array_collid = key->parttypcoll[keynum];
1658  arrexpr->element_typeid = key->parttypid[keynum];
1659  arrexpr->elements = elems;
1660  arrexpr->multidims = false;
1661  arrexpr->location = -1;
1662 
1663  /* Build leftop = ANY (rightop) */
1664  saopexpr = makeNode(ScalarArrayOpExpr);
1665  saopexpr->opno = operoid;
1666  saopexpr->opfuncid = get_opcode(operoid);
1667  saopexpr->useOr = true;
1668  saopexpr->inputcollid = key->partcollation[keynum];
1669  saopexpr->args = list_make2(arg1, arrexpr);
1670  saopexpr->location = -1;
1671 
1672  result = (Expr *) saopexpr;
1673  }
1674  else
1675  {
1676  List *elemops = NIL;
1677  ListCell *lc;
1678 
1679  foreach (lc, elems)
1680  {
1681  Expr *elem = lfirst(lc),
1682  *elemop;
1683 
1684  elemop = make_opclause(operoid,
1685  BOOLOID,
1686  false,
1687  arg1, elem,
1688  InvalidOid,
1689  key->partcollation[keynum]);
1690  elemops = lappend(elemops, elemop);
1691  }
1692 
1693  result = nelems > 1 ? makeBoolExpr(OR_EXPR, elemops, -1) : linitial(elemops);
1694  }
1695  break;
1696  }
1697 
1699  result = make_opclause(operoid,
1700  BOOLOID,
1701  false,
1702  arg1, arg2,
1703  InvalidOid,
1704  key->partcollation[keynum]);
1705  break;
1706 
1707  default:
1708  elog(ERROR, "invalid partitioning strategy");
1709  break;
1710  }
1711 
1712  return result;
1713 }
1714 
1715 /*
1716  * get_qual_for_hash
1717  *
1718  * Given a list of partition columns, modulus and remainder corresponding to a
1719  * partition, this function returns CHECK constraint expression Node for that
1720  * partition.
1721  *
1722  * The partition constraint for a hash partition is always a call to the
1723  * built-in function satisfies_hash_partition(). The first two arguments are
1724  * the modulus and remainder for the partition; the remaining arguments are the
1725  * values to be hashed.
1726  */
1727 static List *
1729 {
1730  PartitionKey key = RelationGetPartitionKey(parent);
1731  FuncExpr *fexpr;
1732  Node *relidConst;
1733  Node *modulusConst;
1734  Node *remainderConst;
1735  List *args;
1736  ListCell *partexprs_item;
1737  int i;
1738 
1739  /* Fixed arguments. */
1740  relidConst = (Node *) makeConst(OIDOID,
1741  -1,
1742  InvalidOid,
1743  sizeof(Oid),
1745  false,
1746  true);
1747 
1748  modulusConst = (Node *) makeConst(INT4OID,
1749  -1,
1750  InvalidOid,
1751  sizeof(int32),
1752  Int32GetDatum(spec->modulus),
1753  false,
1754  true);
1755 
1756  remainderConst = (Node *) makeConst(INT4OID,
1757  -1,
1758  InvalidOid,
1759  sizeof(int32),
1760  Int32GetDatum(spec->remainder),
1761  false,
1762  true);
1763 
1764  args = list_make3(relidConst, modulusConst, remainderConst);
1765  partexprs_item = list_head(key->partexprs);
1766 
1767  /* Add an argument for each key column. */
1768  for (i = 0; i < key->partnatts; i++)
1769  {
1770  Node *keyCol;
1771 
1772  /* Left operand */
1773  if (key->partattrs[i] != 0)
1774  {
1775  keyCol = (Node *) makeVar(1,
1776  key->partattrs[i],
1777  key->parttypid[i],
1778  key->parttypmod[i],
1779  key->parttypcoll[i],
1780  0);
1781  }
1782  else
1783  {
1784  keyCol = (Node *) copyObject(lfirst(partexprs_item));
1785  partexprs_item = lnext(partexprs_item);
1786  }
1787 
1788  args = lappend(args, keyCol);
1789  }
1790 
1791  fexpr = makeFuncExpr(F_SATISFIES_HASH_PARTITION,
1792  BOOLOID,
1793  args,
1794  InvalidOid,
1795  InvalidOid,
1797 
1798  return list_make1(fexpr);
1799 }
1800 
1801 /*
1802  * get_qual_for_list
1803  *
1804  * Returns an implicit-AND list of expressions to use as a list partition's
1805  * constraint, given the partition key and bound structures.
1806  *
1807  * The function returns NIL for a default partition when it's the only
1808  * partition since in that case there is no constraint.
1809  */
1810 static List *
1812 {
1813  PartitionKey key = RelationGetPartitionKey(parent);
1814  List *result;
1815  Expr *keyCol;
1816  Expr *opexpr;
1817  NullTest *nulltest;
1818  ListCell *cell;
1819  List *elems = NIL;
1820  bool list_has_null = false;
1821 
1822  /*
1823  * Only single-column list partitioning is supported, so we are worried
1824  * only about the partition key with index 0.
1825  */
1826  Assert(key->partnatts == 1);
1827 
1828  /* Construct Var or expression representing the partition column */
1829  if (key->partattrs[0] != 0)
1830  keyCol = (Expr *) makeVar(1,
1831  key->partattrs[0],
1832  key->parttypid[0],
1833  key->parttypmod[0],
1834  key->parttypcoll[0],
1835  0);
1836  else
1837  keyCol = (Expr *) copyObject(linitial(key->partexprs));
1838 
1839  /*
1840  * For default list partition, collect datums for all the partitions. The
1841  * default partition constraint should check that the partition key is
1842  * equal to none of those.
1843  */
1844  if (spec->is_default)
1845  {
1846  int i;
1847  int ndatums = 0;
1848  PartitionDesc pdesc = RelationGetPartitionDesc(parent);
1849  PartitionBoundInfo boundinfo = pdesc->boundinfo;
1850 
1851  if (boundinfo)
1852  {
1853  ndatums = boundinfo->ndatums;
1854 
1855  if (partition_bound_accepts_nulls(boundinfo))
1856  list_has_null = true;
1857  }
1858 
1859  /*
1860  * If default is the only partition, there need not be any partition
1861  * constraint on it.
1862  */
1863  if (ndatums == 0 && !list_has_null)
1864  return NIL;
1865 
1866  for (i = 0; i < ndatums; i++)
1867  {
1868  Const *val;
1869 
1870  /*
1871  * Construct Const from known-not-null datum. We must be careful
1872  * to copy the value, because our result has to be able to outlive
1873  * the relcache entry we're copying from.
1874  */
1875  val = makeConst(key->parttypid[0],
1876  key->parttypmod[0],
1877  key->parttypcoll[0],
1878  key->parttyplen[0],
1879  datumCopy(*boundinfo->datums[i],
1880  key->parttypbyval[0],
1881  key->parttyplen[0]),
1882  false, /* isnull */
1883  key->parttypbyval[0]);
1884 
1885  elems = lappend(elems, val);
1886  }
1887  }
1888  else
1889  {
1890  /*
1891  * Create list of Consts for the allowed values, excluding any nulls.
1892  */
1893  foreach(cell, spec->listdatums)
1894  {
1895  Const *val = castNode(Const, lfirst(cell));
1896 
1897  if (val->constisnull)
1898  list_has_null = true;
1899  else
1900  elems = lappend(elems, copyObject(val));
1901  }
1902  }
1903 
1904  if (elems)
1905  {
1906  /*
1907  * Generate the operator expression from the non-null partition
1908  * values.
1909  */
1911  keyCol, (Expr *) elems);
1912  }
1913  else
1914  {
1915  /*
1916  * If there are no partition values, we don't need an operator
1917  * expression.
1918  */
1919  opexpr = NULL;
1920  }
1921 
1922  if (!list_has_null)
1923  {
1924  /*
1925  * Gin up a "col IS NOT NULL" test that will be AND'd with the main
1926  * expression. This might seem redundant, but the partition routing
1927  * machinery needs it.
1928  */
1929  nulltest = makeNode(NullTest);
1930  nulltest->arg = keyCol;
1931  nulltest->nulltesttype = IS_NOT_NULL;
1932  nulltest->argisrow = false;
1933  nulltest->location = -1;
1934 
1935  result = opexpr ? list_make2(nulltest, opexpr) : list_make1(nulltest);
1936  }
1937  else
1938  {
1939  /*
1940  * Gin up a "col IS NULL" test that will be OR'd with the main
1941  * expression.
1942  */
1943  nulltest = makeNode(NullTest);
1944  nulltest->arg = keyCol;
1945  nulltest->nulltesttype = IS_NULL;
1946  nulltest->argisrow = false;
1947  nulltest->location = -1;
1948 
1949  if (opexpr)
1950  {
1951  Expr *or;
1952 
1953  or = makeBoolExpr(OR_EXPR, list_make2(nulltest, opexpr), -1);
1954  result = list_make1(or);
1955  }
1956  else
1957  result = list_make1(nulltest);
1958  }
1959 
1960  /*
1961  * Note that, in general, applying NOT to a constraint expression doesn't
1962  * necessarily invert the set of rows it accepts, because NOT (NULL) is
1963  * NULL. However, the partition constraints we construct here never
1964  * evaluate to NULL, so applying NOT works as intended.
1965  */
1966  if (spec->is_default)
1967  {
1968  result = list_make1(make_ands_explicit(result));
1969  result = list_make1(makeBoolExpr(NOT_EXPR, result, -1));
1970  }
1971 
1972  return result;
1973 }
1974 
1975 /*
1976  * get_range_key_properties
1977  * Returns range partition key information for a given column
1978  *
1979  * This is a subroutine for get_qual_for_range, and its API is pretty
1980  * specialized to that caller.
1981  *
1982  * Constructs an Expr for the key column (returned in *keyCol) and Consts
1983  * for the lower and upper range limits (returned in *lower_val and
1984  * *upper_val). For MINVALUE/MAXVALUE limits, NULL is returned instead of
1985  * a Const. All of these structures are freshly palloc'd.
1986  *
1987  * *partexprs_item points to the cell containing the next expression in
1988  * the key->partexprs list, or NULL. It may be advanced upon return.
1989  */
1990 static void
1992  PartitionRangeDatum *ldatum,
1993  PartitionRangeDatum *udatum,
1994  ListCell **partexprs_item,
1995  Expr **keyCol,
1996  Const **lower_val, Const **upper_val)
1997 {
1998  /* Get partition key expression for this column */
1999  if (key->partattrs[keynum] != 0)
2000  {
2001  *keyCol = (Expr *) makeVar(1,
2002  key->partattrs[keynum],
2003  key->parttypid[keynum],
2004  key->parttypmod[keynum],
2005  key->parttypcoll[keynum],
2006  0);
2007  }
2008  else
2009  {
2010  if (*partexprs_item == NULL)
2011  elog(ERROR, "wrong number of partition key expressions");
2012  *keyCol = copyObject(lfirst(*partexprs_item));
2013  *partexprs_item = lnext(*partexprs_item);
2014  }
2015 
2016  /* Get appropriate Const nodes for the bounds */
2017  if (ldatum->kind == PARTITION_RANGE_DATUM_VALUE)
2018  *lower_val = castNode(Const, copyObject(ldatum->value));
2019  else
2020  *lower_val = NULL;
2021 
2022  if (udatum->kind == PARTITION_RANGE_DATUM_VALUE)
2023  *upper_val = castNode(Const, copyObject(udatum->value));
2024  else
2025  *upper_val = NULL;
2026 }
2027 
2028  /*
2029  * get_range_nulltest
2030  *
2031  * A non-default range partition table does not currently allow partition
2032  * keys to be null, so emit an IS NOT NULL expression for each key column.
2033  */
2034 static List *
2036 {
2037  List *result = NIL;
2038  NullTest *nulltest;
2039  ListCell *partexprs_item;
2040  int i;
2041 
2042  partexprs_item = list_head(key->partexprs);
2043  for (i = 0; i < key->partnatts; i++)
2044  {
2045  Expr *keyCol;
2046 
2047  if (key->partattrs[i] != 0)
2048  {
2049  keyCol = (Expr *) makeVar(1,
2050  key->partattrs[i],
2051  key->parttypid[i],
2052  key->parttypmod[i],
2053  key->parttypcoll[i],
2054  0);
2055  }
2056  else
2057  {
2058  if (partexprs_item == NULL)
2059  elog(ERROR, "wrong number of partition key expressions");
2060  keyCol = copyObject(lfirst(partexprs_item));
2061  partexprs_item = lnext(partexprs_item);
2062  }
2063 
2064  nulltest = makeNode(NullTest);
2065  nulltest->arg = keyCol;
2066  nulltest->nulltesttype = IS_NOT_NULL;
2067  nulltest->argisrow = false;
2068  nulltest->location = -1;
2069  result = lappend(result, nulltest);
2070  }
2071 
2072  return result;
2073 }
2074 
2075 /*
2076  * get_qual_for_range
2077  *
2078  * Returns an implicit-AND list of expressions to use as a range partition's
2079  * constraint, given the partition key and bound structures.
2080  *
2081  * For a multi-column range partition key, say (a, b, c), with (al, bl, cl)
2082  * as the lower bound tuple and (au, bu, cu) as the upper bound tuple, we
2083  * generate an expression tree of the following form:
2084  *
2085  * (a IS NOT NULL) and (b IS NOT NULL) and (c IS NOT NULL)
2086  * AND
2087  * (a > al OR (a = al AND b > bl) OR (a = al AND b = bl AND c >= cl))
2088  * AND
2089  * (a < au OR (a = au AND b < bu) OR (a = au AND b = bu AND c < cu))
2090  *
2091  * It is often the case that a prefix of lower and upper bound tuples contains
2092  * the same values, for example, (al = au), in which case, we will emit an
2093  * expression tree of the following form:
2094  *
2095  * (a IS NOT NULL) and (b IS NOT NULL) and (c IS NOT NULL)
2096  * AND
2097  * (a = al)
2098  * AND
2099  * (b > bl OR (b = bl AND c >= cl))
2100  * AND
2101  * (b < bu) OR (b = bu AND c < cu))
2102  *
2103  * If a bound datum is either MINVALUE or MAXVALUE, these expressions are
2104  * simplified using the fact that any value is greater than MINVALUE and less
2105  * than MAXVALUE. So, for example, if cu = MAXVALUE, c < cu is automatically
2106  * true, and we need not emit any expression for it, and the last line becomes
2107  *
2108  * (b < bu) OR (b = bu), which is simplified to (b <= bu)
2109  *
2110  * In most common cases with only one partition column, say a, the following
2111  * expression tree will be generated: a IS NOT NULL AND a >= al AND a < au
2112  *
2113  * For default partition, it returns the negation of the constraints of all
2114  * the other partitions.
2115  *
2116  * External callers should pass for_default as false; we set it to true only
2117  * when recursing.
2118  */
2119 static List *
2121  bool for_default)
2122 {
2123  List *result = NIL;
2124  ListCell *cell1,
2125  *cell2,
2126  *partexprs_item,
2127  *partexprs_item_saved;
2128  int i,
2129  j;
2130  PartitionRangeDatum *ldatum,
2131  *udatum;
2132  PartitionKey key = RelationGetPartitionKey(parent);
2133  Expr *keyCol;
2134  Const *lower_val,
2135  *upper_val;
2136  List *lower_or_arms,
2137  *upper_or_arms;
2138  int num_or_arms,
2139  current_or_arm;
2140  ListCell *lower_or_start_datum,
2141  *upper_or_start_datum;
2142  bool need_next_lower_arm,
2143  need_next_upper_arm;
2144 
2145  if (spec->is_default)
2146  {
2147  List *or_expr_args = NIL;
2148  PartitionDesc pdesc = RelationGetPartitionDesc(parent);
2149  Oid *inhoids = pdesc->oids;
2150  int nparts = pdesc->nparts,
2151  i;
2152 
2153  for (i = 0; i < nparts; i++)
2154  {
2155  Oid inhrelid = inhoids[i];
2156  HeapTuple tuple;
2157  Datum datum;
2158  bool isnull;
2159  PartitionBoundSpec *bspec;
2160 
2161  tuple = SearchSysCache1(RELOID, inhrelid);
2162  if (!HeapTupleIsValid(tuple))
2163  elog(ERROR, "cache lookup failed for relation %u", inhrelid);
2164 
2165  datum = SysCacheGetAttr(RELOID, tuple,
2167  &isnull);
2168 
2169  Assert(!isnull);
2170  bspec = (PartitionBoundSpec *)
2172  if (!IsA(bspec, PartitionBoundSpec))
2173  elog(ERROR, "expected PartitionBoundSpec");
2174 
2175  if (!bspec->is_default)
2176  {
2177  List *part_qual;
2178 
2179  part_qual = get_qual_for_range(parent, bspec, true);
2180 
2181  /*
2182  * AND the constraints of the partition and add to
2183  * or_expr_args
2184  */
2185  or_expr_args = lappend(or_expr_args, list_length(part_qual) > 1
2186  ? makeBoolExpr(AND_EXPR, part_qual, -1)
2187  : linitial(part_qual));
2188  }
2189  ReleaseSysCache(tuple);
2190  }
2191 
2192  if (or_expr_args != NIL)
2193  {
2194  Expr *other_parts_constr;
2195 
2196  /*
2197  * Combine the constraints obtained for non-default partitions
2198  * using OR. As requested, each of the OR's args doesn't include
2199  * the NOT NULL test for partition keys (which is to avoid its
2200  * useless repetition). Add the same now.
2201  */
2202  other_parts_constr =
2205  list_length(or_expr_args) > 1
2206  ? makeBoolExpr(OR_EXPR, or_expr_args,
2207  -1)
2208  : linitial(or_expr_args)),
2209  -1);
2210 
2211  /*
2212  * Finally, the default partition contains everything *NOT*
2213  * contained in the non-default partitions.
2214  */
2215  result = list_make1(makeBoolExpr(NOT_EXPR,
2216  list_make1(other_parts_constr), -1));
2217  }
2218 
2219  return result;
2220  }
2221 
2222  lower_or_start_datum = list_head(spec->lowerdatums);
2223  upper_or_start_datum = list_head(spec->upperdatums);
2224  num_or_arms = key->partnatts;
2225 
2226  /*
2227  * If it is the recursive call for default, we skip the get_range_nulltest
2228  * to avoid accumulating the NullTest on the same keys for each partition.
2229  */
2230  if (!for_default)
2231  result = get_range_nulltest(key);
2232 
2233  /*
2234  * Iterate over the key columns and check if the corresponding lower and
2235  * upper datums are equal using the btree equality operator for the
2236  * column's type. If equal, we emit single keyCol = common_value
2237  * expression. Starting from the first column for which the corresponding
2238  * lower and upper bound datums are not equal, we generate OR expressions
2239  * as shown in the function's header comment.
2240  */
2241  i = 0;
2242  partexprs_item = list_head(key->partexprs);
2243  partexprs_item_saved = partexprs_item; /* placate compiler */
2244  forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
2245  {
2246  EState *estate;
2247  MemoryContext oldcxt;
2248  Expr *test_expr;
2249  ExprState *test_exprstate;
2250  Datum test_result;
2251  bool isNull;
2252 
2253  ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
2254  udatum = castNode(PartitionRangeDatum, lfirst(cell2));
2255 
2256  /*
2257  * Since get_range_key_properties() modifies partexprs_item, and we
2258  * might need to start over from the previous expression in the later
2259  * part of this function, save away the current value.
2260  */
2261  partexprs_item_saved = partexprs_item;
2262 
2263  get_range_key_properties(key, i, ldatum, udatum,
2264  &partexprs_item,
2265  &keyCol,
2266  &lower_val, &upper_val);
2267 
2268  /*
2269  * If either value is NULL, the corresponding partition bound is
2270  * either MINVALUE or MAXVALUE, and we treat them as unequal, because
2271  * even if they're the same, there is no common value to equate the
2272  * key column with.
2273  */
2274  if (!lower_val || !upper_val)
2275  break;
2276 
2277  /* Create the test expression */
2278  estate = CreateExecutorState();
2279  oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
2280  test_expr = make_partition_op_expr(key, i, BTEqualStrategyNumber,
2281  (Expr *) lower_val,
2282  (Expr *) upper_val);
2283  fix_opfuncids((Node *) test_expr);
2284  test_exprstate = ExecInitExpr(test_expr, NULL);
2285  test_result = ExecEvalExprSwitchContext(test_exprstate,
2286  GetPerTupleExprContext(estate),
2287  &isNull);
2288  MemoryContextSwitchTo(oldcxt);
2289  FreeExecutorState(estate);
2290 
2291  /* If not equal, go generate the OR expressions */
2292  if (!DatumGetBool(test_result))
2293  break;
2294 
2295  /*
2296  * The bounds for the last key column can't be equal, because such a
2297  * range partition would never be allowed to be defined (it would have
2298  * an empty range otherwise).
2299  */
2300  if (i == key->partnatts - 1)
2301  elog(ERROR, "invalid range bound specification");
2302 
2303  /* Equal, so generate keyCol = lower_val expression */
2304  result = lappend(result,
2306  keyCol, (Expr *) lower_val));
2307 
2308  i++;
2309  }
2310 
2311  /* First pair of lower_val and upper_val that are not equal. */
2312  lower_or_start_datum = cell1;
2313  upper_or_start_datum = cell2;
2314 
2315  /* OR will have as many arms as there are key columns left. */
2316  num_or_arms = key->partnatts - i;
2317  current_or_arm = 0;
2318  lower_or_arms = upper_or_arms = NIL;
2319  need_next_lower_arm = need_next_upper_arm = true;
2320  while (current_or_arm < num_or_arms)
2321  {
2322  List *lower_or_arm_args = NIL,
2323  *upper_or_arm_args = NIL;
2324 
2325  /* Restart scan of columns from the i'th one */
2326  j = i;
2327  partexprs_item = partexprs_item_saved;
2328 
2329  for_both_cell(cell1, lower_or_start_datum, cell2, upper_or_start_datum)
2330  {
2331  PartitionRangeDatum *ldatum_next = NULL,
2332  *udatum_next = NULL;
2333 
2334  ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
2335  if (lnext(cell1))
2336  ldatum_next = castNode(PartitionRangeDatum,
2337  lfirst(lnext(cell1)));
2338  udatum = castNode(PartitionRangeDatum, lfirst(cell2));
2339  if (lnext(cell2))
2340  udatum_next = castNode(PartitionRangeDatum,
2341  lfirst(lnext(cell2)));
2342  get_range_key_properties(key, j, ldatum, udatum,
2343  &partexprs_item,
2344  &keyCol,
2345  &lower_val, &upper_val);
2346 
2347  if (need_next_lower_arm && lower_val)
2348  {
2349  uint16 strategy;
2350 
2351  /*
2352  * For the non-last columns of this arm, use the EQ operator.
2353  * For the last column of this arm, use GT, unless this is the
2354  * last column of the whole bound check, or the next bound
2355  * datum is MINVALUE, in which case use GE.
2356  */
2357  if (j - i < current_or_arm)
2358  strategy = BTEqualStrategyNumber;
2359  else if (j == key->partnatts - 1 ||
2360  (ldatum_next &&
2361  ldatum_next->kind == PARTITION_RANGE_DATUM_MINVALUE))
2362  strategy = BTGreaterEqualStrategyNumber;
2363  else
2364  strategy = BTGreaterStrategyNumber;
2365 
2366  lower_or_arm_args = lappend(lower_or_arm_args,
2367  make_partition_op_expr(key, j,
2368  strategy,
2369  keyCol,
2370  (Expr *) lower_val));
2371  }
2372 
2373  if (need_next_upper_arm && upper_val)
2374  {
2375  uint16 strategy;
2376 
2377  /*
2378  * For the non-last columns of this arm, use the EQ operator.
2379  * For the last column of this arm, use LT, unless the next
2380  * bound datum is MAXVALUE, in which case use LE.
2381  */
2382  if (j - i < current_or_arm)
2383  strategy = BTEqualStrategyNumber;
2384  else if (udatum_next &&
2385  udatum_next->kind == PARTITION_RANGE_DATUM_MAXVALUE)
2386  strategy = BTLessEqualStrategyNumber;
2387  else
2388  strategy = BTLessStrategyNumber;
2389 
2390  upper_or_arm_args = lappend(upper_or_arm_args,
2391  make_partition_op_expr(key, j,
2392  strategy,
2393  keyCol,
2394  (Expr *) upper_val));
2395  }
2396 
2397  /*
2398  * Did we generate enough of OR's arguments? First arm considers
2399  * the first of the remaining columns, second arm considers first
2400  * two of the remaining columns, and so on.
2401  */
2402  ++j;
2403  if (j - i > current_or_arm)
2404  {
2405  /*
2406  * We must not emit any more arms if the new column that will
2407  * be considered is unbounded, or this one was.
2408  */
2409  if (!lower_val || !ldatum_next ||
2410  ldatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
2411  need_next_lower_arm = false;
2412  if (!upper_val || !udatum_next ||
2413  udatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
2414  need_next_upper_arm = false;
2415  break;
2416  }
2417  }
2418 
2419  if (lower_or_arm_args != NIL)
2420  lower_or_arms = lappend(lower_or_arms,
2421  list_length(lower_or_arm_args) > 1
2422  ? makeBoolExpr(AND_EXPR, lower_or_arm_args, -1)
2423  : linitial(lower_or_arm_args));
2424 
2425  if (upper_or_arm_args != NIL)
2426  upper_or_arms = lappend(upper_or_arms,
2427  list_length(upper_or_arm_args) > 1
2428  ? makeBoolExpr(AND_EXPR, upper_or_arm_args, -1)
2429  : linitial(upper_or_arm_args));
2430 
2431  /* If no work to do in the next iteration, break away. */
2432  if (!need_next_lower_arm && !need_next_upper_arm)
2433  break;
2434 
2435  ++current_or_arm;
2436  }
2437 
2438  /*
2439  * Generate the OR expressions for each of lower and upper bounds (if
2440  * required), and append to the list of implicitly ANDed list of
2441  * expressions.
2442  */
2443  if (lower_or_arms != NIL)
2444  result = lappend(result,
2445  list_length(lower_or_arms) > 1
2446  ? makeBoolExpr(OR_EXPR, lower_or_arms, -1)
2447  : linitial(lower_or_arms));
2448  if (upper_or_arms != NIL)
2449  result = lappend(result,
2450  list_length(upper_or_arms) > 1
2451  ? makeBoolExpr(OR_EXPR, upper_or_arms, -1)
2452  : linitial(upper_or_arms));
2453 
2454  /*
2455  * As noted above, for non-default, we return list with constant TRUE. If
2456  * the result is NIL during the recursive call for default, it implies
2457  * this is the only other partition which can hold every value of the key
2458  * except NULL. Hence we return the NullTest result skipped earlier.
2459  */
2460  if (result == NIL)
2461  result = for_default
2462  ? get_range_nulltest(key)
2463  : list_make1(makeBoolConst(true, false));
2464 
2465  return result;
2466 }
2467 
2468 /*
2469  * generate_partition_qual
2470  *
2471  * Generate partition predicate from rel's partition bound expression. The
2472  * function returns a NIL list if there is no predicate.
2473  *
2474  * Result expression tree is stored CacheMemoryContext to ensure it survives
2475  * as long as the relcache entry. But we should be running in a less long-lived
2476  * working context. To avoid leaking cache memory if this routine fails partway
2477  * through, we build in working memory and then copy the completed structure
2478  * into cache memory.
2479  */
2480 static List *
2482 {
2483  HeapTuple tuple;
2484  MemoryContext oldcxt;
2485  Datum boundDatum;
2486  bool isnull;
2487  PartitionBoundSpec *bound;
2488  List *my_qual = NIL,
2489  *result = NIL;
2490  Relation parent;
2491  bool found_whole_row;
2492 
2493  /* Guard against stack overflow due to overly deep partition tree */
2495 
2496  /* Quick copy */
2497  if (rel->rd_partcheck != NIL)
2498  return copyObject(rel->rd_partcheck);
2499 
2500  /* Grab at least an AccessShareLock on the parent table */
2502  AccessShareLock);
2503 
2504  /* Get pg_class.relpartbound */
2505  tuple = SearchSysCache1(RELOID, RelationGetRelid(rel));
2506  if (!HeapTupleIsValid(tuple))
2507  elog(ERROR, "cache lookup failed for relation %u",
2508  RelationGetRelid(rel));
2509 
2510  boundDatum = SysCacheGetAttr(RELOID, tuple,
2512  &isnull);
2513  if (isnull) /* should not happen */
2514  elog(ERROR, "relation \"%s\" has relpartbound = null",
2516  bound = castNode(PartitionBoundSpec,
2517  stringToNode(TextDatumGetCString(boundDatum)));
2518  ReleaseSysCache(tuple);
2519 
2520  my_qual = get_qual_from_partbound(rel, parent, bound);
2521 
2522  /* Add the parent's quals to the list (if any) */
2523  if (parent->rd_rel->relispartition)
2524  result = list_concat(generate_partition_qual(parent), my_qual);
2525  else
2526  result = my_qual;
2527 
2528  /*
2529  * Change Vars to have partition's attnos instead of the parent's. We do
2530  * this after we concatenate the parent's quals, because we want every Var
2531  * in it to bear this relation's attnos. It's safe to assume varno = 1
2532  * here.
2533  */
2534  result = map_partition_varattnos(result, 1, rel, parent,
2535  &found_whole_row);
2536  /* There can never be a whole-row reference here */
2537  if (found_whole_row)
2538  elog(ERROR, "unexpected whole-row reference found in partition key");
2539 
2540  /* Save a copy in the relcache */
2542  rel->rd_partcheck = copyObject(result);
2543  MemoryContextSwitchTo(oldcxt);
2544 
2545  /* Keep the parent locked until commit */
2546  heap_close(parent, NoLock);
2547 
2548  return result;
2549 }
2550 
2551 /*
2552  * get_partition_for_tuple
2553  * Finds partition of relation which accepts the partition key specified
2554  * in values and isnull
2555  *
2556  * Return value is index of the partition (>= 0 and < partdesc->nparts) if one
2557  * found or -1 if none found.
2558  */
2559 int
2561 {
2562  int bound_offset;
2563  int part_index = -1;
2564  PartitionKey key = RelationGetPartitionKey(relation);
2565  PartitionDesc partdesc = RelationGetPartitionDesc(relation);
2566 
2567  /* Route as appropriate based on partitioning strategy. */
2568  switch (key->strategy)
2569  {
2571  {
2572  PartitionBoundInfo boundinfo = partdesc->boundinfo;
2573  int greatest_modulus = get_greatest_modulus(boundinfo);
2574  uint64 rowHash = compute_hash_value(key, values, isnull);
2575 
2576  part_index = boundinfo->indexes[rowHash % greatest_modulus];
2577  }
2578  break;
2579 
2581  if (isnull[0])
2582  {
2584  part_index = partdesc->boundinfo->null_index;
2585  }
2586  else
2587  {
2588  bool equal = false;
2589 
2590  bound_offset = partition_list_bsearch(key,
2591  partdesc->boundinfo,
2592  values[0], &equal);
2593  if (bound_offset >= 0 && equal)
2594  part_index = partdesc->boundinfo->indexes[bound_offset];
2595  }
2596  break;
2597 
2599  {
2600  bool equal = false,
2601  range_partkey_has_null = false;
2602  int i;
2603 
2604  /*
2605  * No range includes NULL, so this will be accepted by the
2606  * default partition if there is one, and otherwise rejected.
2607  */
2608  for (i = 0; i < key->partnatts; i++)
2609  {
2610  if (isnull[i])
2611  {
2612  range_partkey_has_null = true;
2613  break;
2614  }
2615  }
2616 
2617  if (!range_partkey_has_null)
2618  {
2619  bound_offset = partition_range_datum_bsearch(key,
2620  partdesc->boundinfo,
2621  key->partnatts,
2622  values,
2623  &equal);
2624  /*
2625  * The bound at bound_offset is less than or equal to the
2626  * tuple value, so the bound at offset+1 is the upper
2627  * bound of the partition we're looking for, if there
2628  * actually exists one.
2629  */
2630  part_index = partdesc->boundinfo->indexes[bound_offset + 1];
2631  }
2632  }
2633  break;
2634 
2635  default:
2636  elog(ERROR, "unexpected partition strategy: %d",
2637  (int) key->strategy);
2638  }
2639 
2640  /*
2641  * part_index < 0 means we failed to find a partition of this parent. Use
2642  * the default partition, if there is one.
2643  */
2644  if (part_index < 0)
2645  part_index = partdesc->boundinfo->default_index;
2646 
2647  return part_index;
2648 }
2649 
2650 /*
2651  * Checks if any of the 'attnums' is a partition key attribute for rel
2652  *
2653  * Sets *used_in_expr if any of the 'attnums' is found to be referenced in some
2654  * partition key expression. It's possible for a column to be both used
2655  * directly and as part of an expression; if that happens, *used_in_expr may
2656  * end up as either true or false. That's OK for current uses of this
2657  * function, because *used_in_expr is only used to tailor the error message
2658  * text.
2659  */
2660 bool
2662  bool *used_in_expr)
2663 {
2664  PartitionKey key;
2665  int partnatts;
2666  List *partexprs;
2667  ListCell *partexprs_item;
2668  int i;
2669 
2670  if (attnums == NULL || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2671  return false;
2672 
2673  key = RelationGetPartitionKey(rel);
2674  partnatts = get_partition_natts(key);
2675  partexprs = get_partition_exprs(key);
2676 
2677  partexprs_item = list_head(partexprs);
2678  for (i = 0; i < partnatts; i++)
2679  {
2680  AttrNumber partattno = get_partition_col_attnum(key, i);
2681 
2682  if (partattno != 0)
2683  {
2685  attnums))
2686  {
2687  if (used_in_expr)
2688  *used_in_expr = false;
2689  return true;
2690  }
2691  }
2692  else
2693  {
2694  /* Arbitrary expression */
2695  Node *expr = (Node *) lfirst(partexprs_item);
2696  Bitmapset *expr_attrs = NULL;
2697 
2698  /* Find all attributes referenced */
2699  pull_varattnos(expr, 1, &expr_attrs);
2700  partexprs_item = lnext(partexprs_item);
2701 
2702  if (bms_overlap(attnums, expr_attrs))
2703  {
2704  if (used_in_expr)
2705  *used_in_expr = true;
2706  return true;
2707  }
2708  }
2709  }
2710 
2711  return false;
2712 }
2713 
2714 /*
2715  * qsort_partition_hbound_cmp
2716  *
2717  * We sort hash bounds by modulus, then by remainder.
2718  */
2719 static int32
2720 qsort_partition_hbound_cmp(const void *a, const void *b)
2721 {
2722  PartitionHashBound *h1 = (*(PartitionHashBound *const *) a);
2723  PartitionHashBound *h2 = (*(PartitionHashBound *const *) b);
2724 
2725  return partition_hbound_cmp(h1->modulus, h1->remainder,
2726  h2->modulus, h2->remainder);
2727 }
2728 
2729 /*
2730  * partition_hbound_cmp
2731  *
2732  * Compares modulus first, then remainder if modulus are equal.
2733  */
2734 static int32
2735 partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
2736 {
2737  if (modulus1 < modulus2)
2738  return -1;
2739  if (modulus1 > modulus2)
2740  return 1;
2741  if (modulus1 == modulus2 && remainder1 != remainder2)
2742  return (remainder1 > remainder2) ? 1 : -1;
2743  return 0;
2744 }
2745 
2746 /*
2747  * qsort_partition_list_value_cmp
2748  *
2749  * Compare two list partition bound datums
2750  */
2751 static int32
2752 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
2753 {
2754  Datum val1 = (*(const PartitionListValue **) a)->value,
2755  val2 = (*(const PartitionListValue **) b)->value;
2756  PartitionKey key = (PartitionKey) arg;
2757 
2759  key->partcollation[0],
2760  val1, val2));
2761 }
2762 
2763 /*
2764  * make_one_range_bound
2765  *
2766  * Return a PartitionRangeBound given a list of PartitionRangeDatum elements
2767  * and a flag telling whether the bound is lower or not. Made into a function
2768  * because there are multiple sites that want to use this facility.
2769  */
2770 static PartitionRangeBound *
2772 {
2773  PartitionRangeBound *bound;
2774  ListCell *lc;
2775  int i;
2776 
2777  Assert(datums != NIL);
2778 
2779  bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
2780  bound->index = index;
2781  bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
2782  bound->kind = (PartitionRangeDatumKind *) palloc0(key->partnatts *
2783  sizeof(PartitionRangeDatumKind));
2784  bound->lower = lower;
2785 
2786  i = 0;
2787  foreach(lc, datums)
2788  {
2790 
2791  /* What's contained in this range datum? */
2792  bound->kind[i] = datum->kind;
2793 
2794  if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
2795  {
2796  Const *val = castNode(Const, datum->value);
2797 
2798  if (val->constisnull)
2799  elog(ERROR, "invalid range bound datum");
2800  bound->datums[i] = val->constvalue;
2801  }
2802 
2803  i++;
2804  }
2805 
2806  return bound;
2807 }
2808 
2809 /* Used when sorting range bounds across all range partitions */
2810 static int32
2811 qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
2812 {
2813  PartitionRangeBound *b1 = (*(PartitionRangeBound *const *) a);
2814  PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
2815  PartitionKey key = (PartitionKey) arg;
2816 
2817  return partition_rbound_cmp(key, b1->datums, b1->kind, b1->lower, b2);
2818 }
2819 
2820 /*
2821  * partition_rbound_cmp
2822  *
2823  * Return for two range bounds whether the 1st one (specified in datums1,
2824  * kind1, and lower1) is <, =, or > the bound specified in *b2.
2825  *
2826  * Note that if the values of the two range bounds compare equal, then we take
2827  * into account whether they are upper or lower bounds, and an upper bound is
2828  * considered to be smaller than a lower bound. This is important to the way
2829  * that RelationBuildPartitionDesc() builds the PartitionBoundInfoData
2830  * structure, which only stores the upper bound of a common boundary between
2831  * two contiguous partitions.
2832  */
2833 static int32
2835  Datum *datums1, PartitionRangeDatumKind *kind1,
2836  bool lower1, PartitionRangeBound *b2)
2837 {
2838  int32 cmpval = 0; /* placate compiler */
2839  int i;
2840  Datum *datums2 = b2->datums;
2841  PartitionRangeDatumKind *kind2 = b2->kind;
2842  bool lower2 = b2->lower;
2843 
2844  for (i = 0; i < key->partnatts; i++)
2845  {
2846  /*
2847  * First, handle cases where the column is unbounded, which should not
2848  * invoke the comparison procedure, and should not consider any later
2849  * columns. Note that the PartitionRangeDatumKind enum elements
2850  * compare the same way as the values they represent.
2851  */
2852  if (kind1[i] < kind2[i])
2853  return -1;
2854  else if (kind1[i] > kind2[i])
2855  return 1;
2856  else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
2857 
2858  /*
2859  * The column bounds are both MINVALUE or both MAXVALUE. No later
2860  * columns should be considered, but we still need to compare
2861  * whether they are upper or lower bounds.
2862  */
2863  break;
2864 
2865  cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
2866  key->partcollation[i],
2867  datums1[i],
2868  datums2[i]));
2869  if (cmpval != 0)
2870  break;
2871  }
2872 
2873  /*
2874  * If the comparison is anything other than equal, we're done. If they
2875  * compare equal though, we still have to consider whether the boundaries
2876  * are inclusive or exclusive. Exclusive one is considered smaller of the
2877  * two.
2878  */
2879  if (cmpval == 0 && lower1 != lower2)
2880  cmpval = lower1 ? 1 : -1;
2881 
2882  return cmpval;
2883 }
2884 
2885 /*
2886  * partition_rbound_datum_cmp
2887  *
2888  * Return whether range bound (specified in rb_datums, rb_kind, and rb_lower)
2889  * is <, =, or > partition key of tuple (tuple_datums)
2890  */
2891 static int32
2893  Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
2894  Datum *tuple_datums, int n_tuple_datums)
2895 {
2896  int i;
2897  int32 cmpval = -1;
2898 
2899  for (i = 0; i < n_tuple_datums; i++)
2900  {
2901  if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
2902  return -1;
2903  else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
2904  return 1;
2905 
2906  cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
2907  key->partcollation[i],
2908  rb_datums[i],
2909  tuple_datums[i]));
2910  if (cmpval != 0)
2911  break;
2912  }
2913 
2914  return cmpval;
2915 }
2916 
2917 /*
2918  * partition_list_bsearch
2919  * Returns the index of the greatest bound datum that is less than equal
2920  * to the given value or -1 if all of the bound datums are greater
2921  *
2922  * *is_equal is set to true if the bound datum at the returned index is equal
2923  * to the input value.
2924  */
2925 static int
2927  PartitionBoundInfo boundinfo,
2928  Datum value, bool *is_equal)
2929 {
2930  int lo,
2931  hi,
2932  mid;
2933 
2934  lo = -1;
2935  hi = boundinfo->ndatums - 1;
2936  while (lo < hi)
2937  {
2938  int32 cmpval;
2939 
2940  mid = (lo + hi + 1) / 2;
2941  cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
2942  key->partcollation[0],
2943  boundinfo->datums[mid][0],
2944  value));
2945  if (cmpval <= 0)
2946  {
2947  lo = mid;
2948  *is_equal = (cmpval == 0);
2949  if (*is_equal)
2950  break;
2951  }
2952  else
2953  hi = mid - 1;
2954  }
2955 
2956  return lo;
2957 }
2958 
2959 /*
2960  * partition_range_bsearch
2961  * Returns the index of the greatest range bound that is less than or
2962  * equal to the given range bound or -1 if all of the range bounds are
2963  * greater
2964  *
2965  * *is_equal is set to true if the range bound at the returned index is equal
2966  * to the input range bound
2967  */
2968 static int
2970  PartitionBoundInfo boundinfo,
2971  PartitionRangeBound *probe, bool *is_equal)
2972 {
2973  int lo,
2974  hi,
2975  mid;
2976 
2977  lo = -1;
2978  hi = boundinfo->ndatums - 1;
2979  while (lo < hi)
2980  {
2981  int32 cmpval;
2982 
2983  mid = (lo + hi + 1) / 2;
2984  cmpval = partition_rbound_cmp(key,
2985  boundinfo->datums[mid],
2986  boundinfo->kind[mid],
2987  (boundinfo->indexes[mid] == -1),
2988  probe);
2989  if (cmpval <= 0)
2990  {
2991  lo = mid;
2992  *is_equal = (cmpval == 0);
2993 
2994  if (*is_equal)
2995  break;
2996  }
2997  else
2998  hi = mid - 1;
2999  }
3000 
3001  return lo;
3002 }
3003 
3004 /*
3005  * partition_range_bsearch
3006  * Returns the index of the greatest range bound that is less than or
3007  * equal to the given tuple or -1 if all of the range bounds are greater
3008  *
3009  * *is_equal is set to true if the range bound at the returned index is equal
3010  * to the input tuple.
3011  */
3012 static int
3014  PartitionBoundInfo boundinfo,
3015  int nvalues, Datum *values, bool *is_equal)
3016 {
3017  int lo,
3018  hi,
3019  mid;
3020 
3021  lo = -1;
3022  hi = boundinfo->ndatums - 1;
3023  while (lo < hi)
3024  {
3025  int32 cmpval;
3026 
3027  mid = (lo + hi + 1) / 2;
3028  cmpval = partition_rbound_datum_cmp(key,
3029  boundinfo->datums[mid],
3030  boundinfo->kind[mid],
3031  values,
3032  nvalues);
3033  if (cmpval <= 0)
3034  {
3035  lo = mid;
3036  *is_equal = (cmpval == 0);
3037 
3038  if (*is_equal)
3039  break;
3040  }
3041  else
3042  hi = mid - 1;
3043  }
3044 
3045  return lo;
3046 }
3047 
3048 /*
3049  * partition_hash_bsearch
3050  * Returns the index of the greatest (modulus, remainder) pair that is
3051  * less than or equal to the given (modulus, remainder) pair or -1 if
3052  * all of them are greater
3053  */
3054 static int
3056  PartitionBoundInfo boundinfo,
3057  int modulus, int remainder)
3058 {
3059  int lo,
3060  hi,
3061  mid;
3062 
3063  lo = -1;
3064  hi = boundinfo->ndatums - 1;
3065  while (lo < hi)
3066  {
3067  int32 cmpval,
3068  bound_modulus,
3069  bound_remainder;
3070 
3071  mid = (lo + hi + 1) / 2;
3072  bound_modulus = DatumGetInt32(boundinfo->datums[mid][0]);
3073  bound_remainder = DatumGetInt32(boundinfo->datums[mid][1]);
3074  cmpval = partition_hbound_cmp(bound_modulus, bound_remainder,
3075  modulus, remainder);
3076  if (cmpval <= 0)
3077  {
3078  lo = mid;
3079 
3080  if (cmpval == 0)
3081  break;
3082  }
3083  else
3084  hi = mid - 1;
3085  }
3086 
3087  return lo;
3088 }
3089 
3090 /*
3091  * get_default_oid_from_partdesc
3092  *
3093  * Given a partition descriptor, return the OID of the default partition, if
3094  * one exists; else, return InvalidOid.
3095  */
3096 Oid
3098 {
3099  if (partdesc && partdesc->boundinfo &&
3101  return partdesc->oids[partdesc->boundinfo->default_index];
3102 
3103  return InvalidOid;
3104 }
3105 
3106 /*
3107  * get_default_partition_oid
3108  *
3109  * Given a relation OID, return the OID of the default partition, if one
3110  * exists. Use get_default_oid_from_partdesc where possible, for
3111  * efficiency.
3112  */
3113 Oid
3115 {
3116  HeapTuple tuple;
3117  Oid defaultPartId = InvalidOid;
3118 
3119  tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(parentId));
3120 
3121  if (HeapTupleIsValid(tuple))
3122  {
3123  Form_pg_partitioned_table part_table_form;
3124 
3125  part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
3126  defaultPartId = part_table_form->partdefid;
3127  ReleaseSysCache(tuple);
3128  }
3129 
3130  return defaultPartId;
3131 }
3132 
3133 /*
3134  * update_default_partition_oid
3135  *
3136  * Update pg_partition_table.partdefid with a new default partition OID.
3137  */
3138 void
3139 update_default_partition_oid(Oid parentId, Oid defaultPartId)
3140 {
3141  HeapTuple tuple;
3142  Relation pg_partitioned_table;
3143  Form_pg_partitioned_table part_table_form;
3144 
3145  pg_partitioned_table = heap_open(PartitionedRelationId, RowExclusiveLock);
3146 
3147  tuple = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(parentId));
3148 
3149  if (!HeapTupleIsValid(tuple))
3150  elog(ERROR, "cache lookup failed for partition key of relation %u",
3151  parentId);
3152 
3153  part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
3154  part_table_form->partdefid = defaultPartId;
3155  CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple);
3156 
3157  heap_freetuple(tuple);
3158  heap_close(pg_partitioned_table, RowExclusiveLock);
3159 }
3160 
3161 /*
3162  * get_proposed_default_constraint
3163  *
3164  * This function returns the negation of new_part_constraints, which
3165  * would be an integral part of the default partition constraints after
3166  * addition of the partition to which the new_part_constraints belongs.
3167  */
3168 List *
3170 {
3171  Expr *defPartConstraint;
3172 
3173  defPartConstraint = make_ands_explicit(new_part_constraints);
3174 
3175  /*
3176  * Derive the partition constraints of default partition by negating the
3177  * given partition constraints. The partition constraint never evaluates
3178  * to NULL, so negating it like this is safe.
3179  */
3180  defPartConstraint = makeBoolExpr(NOT_EXPR,
3181  list_make1(defPartConstraint),
3182  -1);
3183  defPartConstraint =
3184  (Expr *) eval_const_expressions(NULL,
3185  (Node *) defPartConstraint);
3186  defPartConstraint = canonicalize_qual(defPartConstraint);
3187 
3188  return list_make1(defPartConstraint);
3189 }
3190 
3191 /*
3192  * get_partition_bound_num_indexes
3193  *
3194  * Returns the number of the entries in the partition bound indexes array.
3195  */
3196 static int
3198 {
3199  int num_indexes;
3200 
3201  Assert(bound);
3202 
3203  switch (bound->strategy)
3204  {
3206 
3207  /*
3208  * The number of the entries in the indexes array is same as the
3209  * greatest modulus.
3210  */
3211  num_indexes = get_greatest_modulus(bound);
3212  break;
3213 
3215  num_indexes = bound->ndatums;
3216  break;
3217 
3219  /* Range partitioned table has an extra index. */
3220  num_indexes = bound->ndatums + 1;
3221  break;
3222 
3223  default:
3224  elog(ERROR, "unexpected partition strategy: %d",
3225  (int) bound->strategy);
3226  }
3227 
3228  return num_indexes;
3229 }
3230 
3231 /*
3232  * get_greatest_modulus
3233  *
3234  * Returns the greatest modulus of the hash partition bound. The greatest
3235  * modulus will be at the end of the datums array because hash partitions are
3236  * arranged in the ascending order of their modulus and remainders.
3237  */
3238 static int
3240 {
3241  Assert(bound && bound->strategy == PARTITION_STRATEGY_HASH);
3242  Assert(bound->datums && bound->ndatums > 0);
3243  Assert(DatumGetInt32(bound->datums[bound->ndatums - 1][0]) > 0);
3244 
3245  return DatumGetInt32(bound->datums[bound->ndatums - 1][0]);
3246 }
3247 
3248 /*
3249  * compute_hash_value
3250  *
3251  * Compute the hash value for given not null partition key values.
3252  */
3253 static uint64
3255 {
3256  int i;
3257  int nkeys = key->partnatts;
3258  uint64 rowHash = 0;
3260 
3261  for (i = 0; i < nkeys; i++)
3262  {
3263  if (!isnull[i])
3264  {
3265  Datum hash;
3266 
3267  Assert(OidIsValid(key->partsupfunc[i].fn_oid));
3268 
3269  /*
3270  * Compute hash for each datum value by calling respective
3271  * datatype-specific hash functions of each partition key
3272  * attribute.
3273  */
3274  hash = FunctionCall2(&key->partsupfunc[i], values[i], seed);
3275 
3276  /* Form a single 64-bit hash value */
3277  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
3278  }
3279  }
3280 
3281  return rowHash;
3282 }
3283 
3284 /*
3285  * satisfies_hash_partition
3286  *
3287  * This is an SQL-callable function for use in hash partition constraints.
3288  * The first three arguments are the parent table OID, modulus, and remainder.
3289  * The remaining arguments are the value of the partitioning columns (or
3290  * expressions); these are hashed and the results are combined into a single
3291  * hash value by calling hash_combine64.
3292  *
3293  * Returns true if remainder produced when this computed single hash value is
3294  * divided by the given modulus is equal to given remainder, otherwise false.
3295  *
3296  * See get_qual_for_hash() for usage.
3297  */
3298 Datum
3300 {
3301  typedef struct ColumnsHashData
3302  {
3303  Oid relid;
3304  int nkeys;
3305  Oid variadic_type;
3306  int16 variadic_typlen;
3307  bool variadic_typbyval;
3308  char variadic_typalign;
3309  FmgrInfo partsupfunc[PARTITION_MAX_KEYS];
3310  } ColumnsHashData;
3311  Oid parentId;
3312  int modulus;
3313  int remainder;
3315  ColumnsHashData *my_extra;
3316  uint64 rowHash = 0;
3317 
3318  /* Return null if the parent OID, modulus, or remainder is NULL. */
3319  if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2))
3320  PG_RETURN_NULL();
3321  parentId = PG_GETARG_OID(0);
3322  modulus = PG_GETARG_INT32(1);
3323  remainder = PG_GETARG_INT32(2);
3324 
3325  /* Sanity check modulus and remainder. */
3326  if (modulus <= 0)
3327  ereport(ERROR,
3328  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3329  errmsg("modulus for hash partition must be a positive integer")));
3330  if (remainder < 0)
3331  ereport(ERROR,
3332  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3333  errmsg("remainder for hash partition must be a non-negative integer")));
3334  if (remainder >= modulus)
3335  ereport(ERROR,
3336  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3337  errmsg("remainder for hash partition must be less than modulus")));
3338 
3339  /*
3340  * Cache hash function information.
3341  */
3342  my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
3343  if (my_extra == NULL || my_extra->relid != parentId)
3344  {
3345  Relation parent;
3346  PartitionKey key;
3347  int j;
3348 
3349  /* Open parent relation and fetch partition keyinfo */
3350  parent = try_relation_open(parentId, AccessShareLock);
3351  if (parent == NULL)
3352  PG_RETURN_NULL();
3353  key = RelationGetPartitionKey(parent);
3354 
3355  /* Reject parent table that is not hash-partitioned. */
3356  if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE ||
3358  ereport(ERROR,
3359  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3360  errmsg("\"%s\" is not a hash partitioned table",
3361  get_rel_name(parentId))));
3362 
3363  if (!get_fn_expr_variadic(fcinfo->flinfo))
3364  {
3365  int nargs = PG_NARGS() - 3;
3366 
3367  /* complain if wrong number of column values */
3368  if (key->partnatts != nargs)
3369  ereport(ERROR,
3370  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3371  errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
3372  key->partnatts, nargs)));
3373 
3374  /* allocate space for our cache */
3375  fcinfo->flinfo->fn_extra =
3376  MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
3377  offsetof(ColumnsHashData, partsupfunc) +
3378  sizeof(FmgrInfo) * nargs);
3379  my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
3380  my_extra->relid = parentId;
3381  my_extra->nkeys = key->partnatts;
3382 
3383  /* check argument types and save fmgr_infos */
3384  for (j = 0; j < key->partnatts; ++j)
3385  {
3386  Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, j + 3);
3387 
3388  if (argtype != key->parttypid[j] && !IsBinaryCoercible(argtype, key->parttypid[j]))
3389  ereport(ERROR,
3390  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3391  errmsg("column %d of the partition key has type \"%s\", but supplied value is of type \"%s\"",
3392  j + 1, format_type_be(key->parttypid[j]), format_type_be(argtype))));
3393 
3394  fmgr_info_copy(&my_extra->partsupfunc[j],
3395  &key->partsupfunc[j],
3396  fcinfo->flinfo->fn_mcxt);
3397  }
3398 
3399  }
3400  else
3401  {
3402  ArrayType *variadic_array = PG_GETARG_ARRAYTYPE_P(3);
3403 
3404  /* allocate space for our cache -- just one FmgrInfo in this case */
3405  fcinfo->flinfo->fn_extra =
3406  MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
3407  offsetof(ColumnsHashData, partsupfunc) +
3408  sizeof(FmgrInfo));
3409  my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
3410  my_extra->relid = parentId;
3411  my_extra->nkeys = key->partnatts;
3412  my_extra->variadic_type = ARR_ELEMTYPE(variadic_array);
3413  get_typlenbyvalalign(my_extra->variadic_type,
3414  &my_extra->variadic_typlen,
3415  &my_extra->variadic_typbyval,
3416  &my_extra->variadic_typalign);
3417 
3418  /* check argument types */
3419  for (j = 0; j < key->partnatts; ++j)
3420  if (key->parttypid[j] != my_extra->variadic_type)
3421  ereport(ERROR,
3422  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3423  errmsg("column %d of the partition key has type \"%s\", but supplied value is of type \"%s\"",
3424  j + 1,
3425  format_type_be(key->parttypid[j]),
3426  format_type_be(my_extra->variadic_type))));
3427 
3428  fmgr_info_copy(&my_extra->partsupfunc[0],
3429  &key->partsupfunc[0],
3430  fcinfo->flinfo->fn_mcxt);
3431  }
3432 
3433  /* Hold lock until commit */
3434  relation_close(parent, NoLock);
3435  }
3436 
3437  if (!OidIsValid(my_extra->variadic_type))
3438  {
3439  int nkeys = my_extra->nkeys;
3440  int i;
3441 
3442  /*
3443  * For a non-variadic call, neither the number of arguments nor their
3444  * types can change across calls, so avoid the expense of rechecking
3445  * here.
3446  */
3447 
3448  for (i = 0; i < nkeys; i++)
3449  {
3450  Datum hash;
3451 
3452  /* keys start from fourth argument of function. */
3453  int argno = i + 3;
3454 
3455  if (PG_ARGISNULL(argno))
3456  continue;
3457 
3458  Assert(OidIsValid(my_extra->partsupfunc[i].fn_oid));
3459 
3460  hash = FunctionCall2(&my_extra->partsupfunc[i],
3461  PG_GETARG_DATUM(argno),
3462  seed);
3463 
3464  /* Form a single 64-bit hash value */
3465  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
3466  }
3467  }
3468  else
3469  {
3470  ArrayType *variadic_array = PG_GETARG_ARRAYTYPE_P(3);
3471  int i;
3472  int nelems;
3473  Datum *datum;
3474  bool *isnull;
3475 
3476  deconstruct_array(variadic_array,
3477  my_extra->variadic_type,
3478  my_extra->variadic_typlen,
3479  my_extra->variadic_typbyval,
3480  my_extra->variadic_typalign,
3481  &datum, &isnull, &nelems);
3482 
3483  /* complain if wrong number of column values */
3484  if (nelems != my_extra->nkeys)
3485  ereport(ERROR,
3486  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3487  errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
3488  my_extra->nkeys, nelems)));
3489 
3490  for (i = 0; i < nelems; i++)
3491  {
3492  Datum hash;
3493 
3494  if (isnull[i])
3495  continue;
3496 
3497  Assert(OidIsValid(my_extra->partsupfunc[0].fn_oid));
3498 
3499  hash = FunctionCall2(&my_extra->partsupfunc[0],
3500  datum[i],
3501  seed);
3502 
3503  /* Form a single 64-bit hash value */
3504  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
3505  }
3506  }
3507 
3508  PG_RETURN_BOOL(rowHash % modulus == remainder);
3509 }
Datum constvalue
Definition: primnodes.h:196
#define list_make2(x1, x2)
Definition: pg_list.h:140
#define list_make3(x1, x2, x3)
Definition: pg_list.h:141
signed short int16
Definition: c.h:301
bool multidims
Definition: primnodes.h:960
#define NIL
Definition: pg_list.h:69
TupleTableSlot * ExecStoreTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree)
Definition: execTuples.c:356
#define PG_GETARG_INT32(n)
Definition: fmgr.h:234
Definition: fmgr.h:56
struct PartitionDescData * rd_partdesc
Definition: rel.h:131
#define Anum_pg_inherits_inhrelid
Definition: pg_inherits.h:50
void * stringToNode(char *str)
Definition: read.c:38
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:110
PartitionRangeDatumKind ** kind
Definition: partition.c:99
#define IsA(nodeptr, _type_)
Definition: nodes.h:564
static Expr * make_partition_op_expr(PartitionKey key, int keynum, uint16 strategy, Expr *arg1, Expr *arg2)
Definition: partition.c:1612
PartitionRangeDatumKind * kind
Definition: partition.c:137
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:297
int get_partition_for_tuple(Relation relation, Datum *values, bool *isnull)
Definition: partition.c:2560
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:180
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:499
#define GETSTRUCT(TUP)
Definition: htup_details.h:661
static struct @130 value
MemoryContext fn_mcxt
Definition: fmgr.h:65
void heap_endscan(HeapScanDesc scan)
Definition: heapam.c:1568
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:863
static int32 qsort_partition_hbound_cmp(const void *a, const void *b)
Definition: partition.c:2720
#define DatumGetInt32(X)
Definition: postgres.h:455
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2991
#define RelationGetDescr(relation)
Definition: rel.h:437
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1153
#define MEMCONTEXT_COPY_NAME
Definition: memutils.h:188
Oid * partopfamily
Definition: rel.h:61
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:44
FmgrInfo * partsupfunc
Definition: rel.h:63
static int get_greatest_modulus(PartitionBoundInfo b)
Definition: partition.c:3239
#define castNode(_type_, nodeptr)
Definition: nodes.h:582
#define OIDOID
Definition: pg_type.h:328
PartitionRangeDatumKind
Definition: parsenodes.h:831
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition: lsyscache.c:2025
#define PG_GETARG_DATUM(n)
Definition: fmgr.h:233
Oid get_array_type(Oid typid)
Definition: lsyscache.c:2530
void fix_opfuncids(Node *node)
Definition: nodeFuncs.c:1582
#define UInt64GetDatum(X)
Definition: postgres.h:631
#define RelationGetForm(relation)
Definition: rel.h:419
static int partition_hash_bsearch(PartitionKey key, PartitionBoundInfo boundinfo, int modulus, int remainder)
Definition: partition.c:3055
Datum satisfies_hash_partition(PG_FUNCTION_ARGS)
Definition: partition.c:3299
static Oid get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel)
Definition: partition.c:1562
bool datumIsEqual(Datum value1, Datum value2, bool typByVal, int typLen)
Definition: datum.c:219
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define FunctionCall2(flinfo, arg1, arg2)
Definition: fmgr.h:605
static int32 qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
Definition: partition.c:2811
void update_default_partition_oid(Oid parentId, Oid defaultPartId)
Definition: partition.c:3139
PartitionRangeDatumKind kind
Definition: parsenodes.h:842
#define AccessShareLock
Definition: lockdefs.h:36
#define INT4OID
Definition: pg_type.h:316
#define InvalidBuffer
Definition: buf.h:25
#define gettext_noop(x)
Definition: c.h:1025
Definition: nodes.h:513
#define partition_bound_accepts_nulls(bi)
Definition: partition.c:109
struct cursor * cur
Definition: ecpg.c:28
bool get_fn_expr_variadic(FmgrInfo *flinfo)
Definition: fmgr.c:2038
uint16 StrategyNumber
Definition: stratnum.h:22
int errcode(int sqlerrcode)
Definition: elog.c:575
#define PARTITION_MAX_KEYS
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: heapam.c:1266
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2462
Oid array_typeid
Definition: primnodes.h:956
#define INFO
Definition: elog.h:33
char * format_type_be(Oid type_oid)
Definition: format_type.c:320
List * list_concat(List *list1, List *list2)
Definition: list.c:321
struct PartitionListValue PartitionListValue
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:28
Expr * make_opclause(Oid opno, Oid opresulttype, bool opretset, Expr *leftop, Expr *rightop, Oid opcollid, Oid inputcollid)
Definition: clauses.c:175
static int get_partition_bound_num_indexes(PartitionBoundInfo b)
Definition: partition.c:3197
#define heap_close(r, l)
Definition: heapam.h:97
Datum upper(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:75
List * partexprs
Definition: rel.h:58
char strategy
Definition: rel.h:54
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1042
Form_pg_class rd_rel
Definition: rel.h:114
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1373
unsigned int Oid
Definition: postgres_ext.h:31
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:298
FormData_pg_partitioned_table * Form_pg_partitioned_table
List * lappend_oid(List *list, Oid datum)
Definition: list.c:164
#define OidIsValid(objectId)
Definition: c.h:594
void check_default_allows_bound(Relation parent, Relation default_rel, PartitionBoundSpec *new_spec)
Definition: partition.c:1232
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:487
List * get_qual_from_partbound(Relation rel, Relation parent, PartitionBoundSpec *spec)
Definition: partition.c:1427
void check_new_partition_bound(char *relname, Relation parent, PartitionBoundSpec *spec)
Definition: partition.c:953
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:328
void RelationBuildPartitionDesc(Relation rel)
Definition: partition.c:203
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:219
bool partition_bounds_equal(int partnatts, int16 *parttyplen, bool *parttypbyval, PartitionBoundInfo b1, PartitionBoundInfo b2)
Definition: partition.c:750
static int32 partition_rbound_datum_cmp(PartitionKey key, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *tuple_datums, int n_tuple_datums)
Definition: partition.c:2892
signed int int32
Definition: c.h:302
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:368
PartitionBoundInfo boundinfo
Definition: partition.h:40
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
Definition: type.h:89
Expr * make_ands_explicit(List *andclauses)
Definition: clauses.c:370
#define list_make1(x1)
Definition: pg_list.h:139
void FreeExecutorState(EState *estate)
Definition: execUtils.c:185
static int get_partition_natts(PartitionKey key)
Definition: rel.h:605
static int16 get_partition_col_attnum(PartitionKey key, int col)
Definition: rel.h:620
#define GetPerTupleExprContext(estate)
Definition: executor.h:490
#define PG_GETARG_ARRAYTYPE_P(n)
Definition: array.h:248
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:416
unsigned short uint16
Definition: c.h:313
void pfree(void *pointer)
Definition: mcxt.c:936
MemoryContext es_query_cxt
Definition: execnodes.h:488
Oid * parttypcoll
Definition: rel.h:74
#define linitial(l)
Definition: pg_list.h:111
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel, bool *found_whole_row)
Definition: partition.c:1478
#define ObjectIdGetDatum(X)
Definition: postgres.h:490
#define ERROR
Definition: elog.h:43
#define partition_bound_has_default(bi)
Definition: partition.c:110
static List * get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
Definition: partition.c:1811
MemoryContext AllocSetContextCreateExtended(MemoryContext parent, const char *name, int flags, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:394
Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)
Definition: fmgr.c:1904
Oid get_partition_parent(Oid relid)
Definition: partition.c:1385
Node * makeBoolConst(bool value, bool isnull)
Definition: makefuncs.c:356
Expr * arg
Definition: primnodes.h:1187
ItemPointerData t_self
Definition: htup.h:65
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:197
void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt)
Definition: fmgr.c:519
static int32 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
Definition: partition.c:2752
struct PartitionHashBound PartitionHashBound
bool has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
Definition: partition.c:2661
char * c
#define NoLock
Definition: lockdefs.h:34
static List * get_partition_exprs(PartitionKey key)
Definition: rel.h:611
List * get_proposed_default_constraint(List *new_part_constraints)
Definition: partition.c:3169
static int32 partition_rbound_cmp(PartitionKey key, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partition.c:2834
RelabelType * makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod, Oid rcollid, CoercionForm rformat)
Definition: makefuncs.c:401
#define PG_GETARG_OID(n)
Definition: fmgr.h:240
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:247
#define PartitionedRelationId
void check_stack_depth(void)
Definition: postgres.c:3154
#define RowExclusiveLock
Definition: lockdefs.h:38
int errdetail(const char *fmt,...)
Definition: elog.c:873
static void get_range_key_properties(PartitionKey key, int keynum, PartitionRangeDatum *ldatum, PartitionRangeDatum *udatum, ListCell **partexprs_item, Expr **keyCol, Const **lower_val, Const **upper_val)
Definition: partition.c:1991
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:163
#define DatumGetBool(X)
Definition: postgres.h:376
#define RelationGetRelationName(relation)
Definition: rel.h:445
static ListCell * list_head(const List *l)
Definition: pg_list.h:77
#define RELKIND_FOREIGN_TABLE
Definition: pg_class.h:167
List * elements
Definition: primnodes.h:959
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc)
Definition: execTuples.c:232
static int partition_range_datum_bsearch(PartitionKey key, PartitionBoundInfo boundinfo, int nvalues, Datum *values, bool *is_equal)
Definition: partition.c:3013
#define lnext(lc)
Definition: pg_list.h:105
#define ereport(elevel, rest)
Definition: elog.h:122
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrNumber *attno_map, int map_length, Oid to_rowtype, bool *found_whole_row)
static List * get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default)
Definition: partition.c:2120
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:67
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:128
Oid * parttypid
Definition: rel.h:69
Oid get_default_partition_oid(Oid parentId)
Definition: partition.c:3114
EState * CreateExecutorState(void)
Definition: execUtils.c:80
static List * get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
Definition: partition.c:1728
char * get_range_partbound_string(List *bound_datums)
Definition: ruleutils.c:11004
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:905
bool IsBinaryCoercible(Oid srctype, Oid targettype)
List * lappend(List *list, void *datum)
Definition: list.c:128
static int32 partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
Definition: partition.c:2735
void qsort_arg(void *base, size_t nel, size_t elsize, qsort_arg_comparator cmp, void *arg)
Definition: qsort_arg.c:113
#define WARNING
Definition: elog.h:40
AttrNumber * convert_tuples_by_name_map(TupleDesc indesc, TupleDesc outdesc, const char *msg)
Definition: tupconvert.c:293
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
static uint64 hash_combine64(uint64 a, uint64 b)
Definition: hashutils.h:29
#define RELKIND_PARTITIONED_TABLE
Definition: pg_class.h:168
Oid * partcollation
Definition: rel.h:66
#define TextDatumGetCString(d)
Definition: builtins.h:92
List * RelationGetPartitionQual(Relation rel)
Definition: partition.c:1511
static PartitionRangeBound * make_one_range_bound(PartitionKey key, int index, List *datums, bool lower)
Definition: partition.c:2771
void * palloc0(Size size)
Definition: mcxt.c:864
int location
Definition: primnodes.h:961
AttrNumber * partattrs
Definition: rel.h:56
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
uintptr_t Datum
Definition: postgres.h:365
static int partition_range_bsearch(PartitionKey key, PartitionBoundInfo boundinfo, PartitionRangeBound *probe, bool *is_equal)
Definition: partition.c:2969
int16 partnatts
Definition: rel.h:55
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
#define Anum_pg_inherits_inhseqno
Definition: pg_inherits.h:52
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1368
Expr * canonicalize_qual(Expr *qual)
Definition: prepqual.c:286
#define list_make1_oid(x1)
Definition: pg_list.h:151
HeapTuple heap_getnext(HeapScanDesc scan, ScanDirection direction)
Definition: heapam.c:1831
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:795
NullTestType nulltesttype
Definition: primnodes.h:1188
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:728
int32 * parttypmod
Definition: rel.h:70
struct PartitionBoundInfoData PartitionBoundInfoData
#define InvalidOid
Definition: postgres_ext.h:36
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1079
List * find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
Definition: pg_inherits.c:57
Oid fn_oid
Definition: fmgr.h:59
#define for_both_cell(cell1, initcell1, cell2, initcell2)
Definition: pg_list.h:194
#define Anum_pg_class_relpartbound
Definition: pg_class.h:135
#define DatumGetUInt64(X)
Definition: postgres.h:617
#define makeNode(_type_)
Definition: nodes.h:561
bool * parttypbyval
Definition: rel.h:72
#define PG_ARGISNULL(n)
Definition: fmgr.h:174
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
MemoryContext rd_pdcxt
Definition: rel.h:130
struct PartitionBoundInfoData * PartitionBoundInfo
Definition: partition.h:31
#define Assert(condition)
Definition: c.h:688
#define lfirst(lc)
Definition: pg_list.h:106
int16 * parttyplen
Definition: rel.h:71
static uint64 compute_hash_value(PartitionKey key, Datum *values, bool *isnull)
Definition: partition.c:3254
Oid array_collid
Definition: primnodes.h:957
#define InheritsRelidSeqnoIndexId
Definition: indexing.h:167
int location
Definition: primnodes.h:1190
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:210
static int partition_list_bsearch(PartitionKey key, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
Definition: partition.c:2926
FormData_pg_inherits * Form_pg_inherits
Definition: pg_inherits.h:43
static int list_length(const List *l)
Definition: pg_list.h:89
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:211
#define type_is_array(typid)
Definition: lsyscache.h:180
#define PG_NARGS()
Definition: fmgr.h:168
#define BOOLOID
Definition: pg_type.h:288
#define HASH_PARTITION_SEED
Definition: partition.h:23
static List * generate_partition_qual(Relation rel)
Definition: partition.c:2481
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:796
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:379
#define RelationGetPartitionKey(relation)
Definition: rel.h:593
PG_FUNCTION_INFO_V1(satisfies_hash_partition)
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:495
Oid element_typeid
Definition: primnodes.h:958
#define InheritsRelationId
Definition: pg_inherits.h:29
Expr * get_partition_qual_relid(Oid relid)
Definition: partition.c:1528
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:487
void deconstruct_array(ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3449
static Datum values[MAXATTR]
Definition: bootstrap.c:164
FormData_pg_class * Form_pg_class
Definition: pg_class.h:95
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:797
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:173
#define AccessExclusiveLock
Definition: lockdefs.h:45
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:167
#define Int32GetDatum(X)
Definition: postgres.h:462
void * palloc(Size size)
Definition: mcxt.c:835
struct PartitionKeyData * PartitionKey
Definition: rel.h:77
int errmsg(const char *fmt,...)
Definition: elog.c:797
Oid * partopcintype
Definition: rel.h:62
int i
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
void * arg
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition: execExpr.c:594
bool argisrow
Definition: primnodes.h:1189
#define PG_FUNCTION_ARGS
Definition: fmgr.h:158
struct PartitionRangeBound PartitionRangeBound
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:118
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98
#define elog
Definition: elog.h:219
PartitionBoundInfo partition_bounds_copy(PartitionBoundInfo src, PartitionKey key)
Definition: partition.c:863
#define qsort(a, b, c, d)
Definition: port.h:408
#define copyObject(obj)
Definition: nodes.h:626
static List * get_range_nulltest(PartitionKey key)
Definition: partition.c:2035
#define BTLessStrategyNumber
Definition: stratnum.h:29
#define RELKIND_RELATION
Definition: pg_class.h:160
Definition: pg_list.h:45
static unsigned hash(unsigned *uv, int n)
Definition: rege_dfa.c:541
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1730
HeapScanDesc heap_beginscan(Relation relation, Snapshot snapshot, int nkeys, ScanKey key)
Definition: heapam.c:1400
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:464
#define ARR_ELEMTYPE(a)
Definition: array.h:277
int16 AttrNumber
Definition: attnum.h:21
#define RelationGetRelid(relation)
Definition: rel.h:425
List * rd_partcheck
Definition: rel.h:132
Oid get_default_oid_from_partdesc(PartitionDesc partdesc)
Definition: partition.c:3097
long val
Definition: informix.c:689
#define PG_RETURN_NULL()
Definition: fmgr.h:305
bool constisnull
Definition: primnodes.h:197
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define offsetof(type, field)
Definition: c.h:611
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32
#define ResetExprContext(econtext)
Definition: executor.h:484
#define lfirst_oid(lc)
Definition: pg_list.h:108
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:13645
#define RelationGetPartitionDesc(relation)
Definition: rel.h:641
FuncExpr * makeFuncExpr(Oid funcid, Oid rettype, List *args, Oid funccollid, Oid inputcollid, CoercionForm fformat)
Definition: makefuncs.c:519
MemoryContext CacheMemoryContext
Definition: mcxt.c:46