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