PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
partition.c File Reference
#include "postgres.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaddress.h"
#include "catalog/partition.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/parsenodes.h"
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
#include "optimizer/var.h"
#include "rewrite/rewriteManip.h"
#include "storage/lmgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/memutils.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/ruleutils.h"
#include "utils/syscache.h"
Include dependency graph for partition.c:

Go to the source code of this file.

Data Structures

struct  PartitionBoundInfoData
 
struct  PartitionListValue
 
struct  PartitionRangeBound
 

Macros

#define partition_bound_accepts_nulls(bi)   ((bi)->null_index != -1)
 
#define APPEND_REL_PARTITION_OIDS(rel, partoids, parents)
 

Typedefs

typedef struct
PartitionBoundInfoData 
PartitionBoundInfoData
 
typedef struct PartitionListValue PartitionListValue
 
typedef struct PartitionRangeBound PartitionRangeBound
 

Functions

static int32 qsort_partition_list_value_cmp (const void *a, const void *b, void *arg)
 
static int32 qsort_partition_rbound_cmp (const void *a, const void *b, void *arg)
 
static Oid get_partition_operator (PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel)
 
static Exprmake_partition_op_expr (PartitionKey key, int keynum, uint16 strategy, Expr *arg1, Expr *arg2)
 
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)
 
static Listget_qual_for_list (PartitionKey key, PartitionBoundSpec *spec)
 
static Listget_qual_for_range (PartitionKey key, PartitionBoundSpec *spec)
 
static Listgenerate_partition_qual (Relation rel)
 
static PartitionRangeBoundmake_one_range_bound (PartitionKey key, int index, List *datums, bool lower)
 
static int32 partition_rbound_cmp (PartitionKey key, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
 
static int32 partition_rbound_datum_cmp (PartitionKey key, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *tuple_datums)
 
static int32 partition_bound_cmp (PartitionKey key, PartitionBoundInfo boundinfo, int offset, void *probe, bool probe_is_bound)
 
static int partition_bound_bsearch (PartitionKey key, PartitionBoundInfo boundinfo, void *probe, bool probe_is_bound, bool *is_equal)
 
void RelationBuildPartitionDesc (Relation rel)
 
bool partition_bounds_equal (int partnatts, int16 *parttyplen, bool *parttypbyval, PartitionBoundInfo b1, PartitionBoundInfo b2)
 
void check_new_partition_bound (char *relname, Relation parent, PartitionBoundSpec *spec)
 
Oid get_partition_parent (Oid relid)
 
Listget_qual_from_partbound (Relation rel, Relation parent, PartitionBoundSpec *spec)
 
Listmap_partition_varattnos (List *expr, int target_varno, Relation partrel, Relation parent, bool *found_whole_row)
 
ListRelationGetPartitionQual (Relation rel)
 
Exprget_partition_qual_relid (Oid relid)
 
PartitionDispatchRelationGetPartitionDispatchInfo (Relation rel, int *num_parted, List **leaf_part_oids)
 
void FormPartitionKeyDatum (PartitionDispatch pd, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
 
int get_partition_for_tuple (PartitionDispatch *pd, TupleTableSlot *slot, EState *estate, PartitionDispatchData **failed_at, TupleTableSlot **failed_slot)
 

Macro Definition Documentation

#define APPEND_REL_PARTITION_OIDS (   rel,
  partoids,
  parents 
)
Value:
do\
{\
int i;\
for (i = 0; i < (rel)->rd_partdesc->nparts; i++)\
{\
(partoids) = lappend_oid((partoids), (rel)->rd_partdesc->oids[i]);\
(parents) = lappend((parents), (rel));\
}\
} while(0)
List * lappend_oid(List *list, Oid datum)
Definition: list.c:164
List * lappend(List *list, void *datum)
Definition: list.c:128
int i

Definition at line 987 of file partition.c.

Referenced by RelationGetPartitionDispatchInfo().

#define partition_bound_accepts_nulls (   bi)    ((bi)->null_index != -1)

Definition at line 85 of file partition.c.

Referenced by check_new_partition_bound(), and get_partition_for_tuple().

Typedef Documentation

Function Documentation

void check_new_partition_bound ( char *  relname,
Relation  parent,
PartitionBoundSpec spec 
)

Definition at line 653 of file partition.c.

References Assert, PartitionDescData::boundinfo, castNode, Const::constisnull, Const::constvalue, PartitionRangeBound::datums, elog, equal(), ereport, errcode(), errdetail(), errmsg(), ERROR, get_range_partbound_string(), get_rel_name(), PartitionBoundInfoData::indexes, PartitionRangeBound::kind, lfirst, PartitionBoundSpec::listdatums, PartitionBoundSpec::location, lower(), PartitionBoundSpec::lowerdatums, make_one_range_bound(), make_parsestate(), PartitionBoundInfoData::ndatums, PartitionDescData::nparts, NULL, PartitionBoundInfoData::null_index, PartitionDescData::oids, parser_errposition(), partition_bound_accepts_nulls, partition_bound_bsearch(), partition_bound_cmp(), partition_rbound_cmp(), PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, RelationGetPartitionDesc, RelationGetPartitionKey, PartitionKeyData::strategy, PartitionBoundInfoData::strategy, PartitionBoundSpec::strategy, upper(), PartitionBoundSpec::upperdatums, and val.

Referenced by ATExecAttachPartition(), and DefineRelation().

655 {
657  PartitionDesc partdesc = RelationGetPartitionDesc(parent);
658  ParseState *pstate = make_parsestate(NULL);
659  int with = -1;
660  bool overlap = false;
661 
662  switch (key->strategy)
663  {
665  {
667 
668  if (partdesc->nparts > 0)
669  {
670  PartitionBoundInfo boundinfo = partdesc->boundinfo;
671  ListCell *cell;
672 
673  Assert(boundinfo &&
674  boundinfo->strategy == PARTITION_STRATEGY_LIST &&
675  (boundinfo->ndatums > 0 ||
676  partition_bound_accepts_nulls(boundinfo)));
677 
678  foreach(cell, spec->listdatums)
679  {
680  Const *val = castNode(Const, lfirst(cell));
681 
682  if (!val->constisnull)
683  {
684  int offset;
685  bool equal;
686 
687  offset = partition_bound_bsearch(key, boundinfo,
688  &val->constvalue,
689  true, &equal);
690  if (offset >= 0 && equal)
691  {
692  overlap = true;
693  with = boundinfo->indexes[offset];
694  break;
695  }
696  }
697  else if (partition_bound_accepts_nulls(boundinfo))
698  {
699  overlap = true;
700  with = boundinfo->null_index;
701  break;
702  }
703  }
704  }
705 
706  break;
707  }
708 
710  {
712  *upper;
713 
715  lower = make_one_range_bound(key, -1, spec->lowerdatums, true);
716  upper = make_one_range_bound(key, -1, spec->upperdatums, false);
717 
718  /*
719  * First check if the resulting range would be empty with
720  * specified lower and upper bounds
721  */
722  if (partition_rbound_cmp(key, lower->datums, lower->kind, true,
723  upper) >= 0)
724  {
725  ereport(ERROR,
726  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
727  errmsg("empty range bound specified for partition \"%s\"",
728  relname),
729  errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
732  parser_errposition(pstate, spec->location)));
733  }
734 
735  if (partdesc->nparts > 0)
736  {
737  PartitionBoundInfo boundinfo = partdesc->boundinfo;
738  int offset;
739  bool equal;
740 
741  Assert(boundinfo && boundinfo->ndatums > 0 &&
742  boundinfo->strategy == PARTITION_STRATEGY_RANGE);
743 
744  /*
745  * Test whether the new lower bound (which is treated
746  * inclusively as part of the new partition) lies inside
747  * an existing partition, or in a gap.
748  *
749  * If it's inside an existing partition, the bound at
750  * offset + 1 will be the upper bound of that partition,
751  * and its index will be >= 0.
752  *
753  * If it's in a gap, the bound at offset + 1 will be the
754  * lower bound of the next partition, and its index will
755  * be -1. This is also true if there is no next partition,
756  * since the index array is initialised with an extra -1
757  * at the end.
758  */
759  offset = partition_bound_bsearch(key, boundinfo, lower,
760  true, &equal);
761 
762  if (boundinfo->indexes[offset + 1] < 0)
763  {
764  /*
765  * Check that the new partition will fit in the gap.
766  * For it to fit, the new upper bound must be less
767  * than or equal to the lower bound of the next
768  * partition, if there is one.
769  */
770  if (offset + 1 < boundinfo->ndatums)
771  {
772  int32 cmpval;
773 
774  cmpval = partition_bound_cmp(key, boundinfo,
775  offset + 1, upper,
776  true);
777  if (cmpval < 0)
778  {
779  /*
780  * The new partition overlaps with the
781  * existing partition between offset + 1 and
782  * offset + 2.
783  */
784  overlap = true;
785  with = boundinfo->indexes[offset + 2];
786  }
787  }
788  }
789  else
790  {
791  /*
792  * The new partition overlaps with the existing
793  * partition between offset and offset + 1.
794  */
795  overlap = true;
796  with = boundinfo->indexes[offset + 1];
797  }
798  }
799 
800  break;
801  }
802 
803  default:
804  elog(ERROR, "unexpected partition strategy: %d",
805  (int) key->strategy);
806  }
807 
808  if (overlap)
809  {
810  Assert(with >= 0);
811  ereport(ERROR,
812  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
813  errmsg("partition \"%s\" would overlap partition \"%s\"",
814  relname, get_rel_name(partdesc->oids[with])),
815  parser_errposition(pstate, spec->location)));
816  }
817 }
Datum constvalue
Definition: primnodes.h:196
PartitionRangeDatumKind * kind
Definition: partition.c:104
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2962
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:43
#define castNode(_type_, nodeptr)
Definition: nodes.h:578
#define partition_bound_accepts_nulls(bi)
Definition: partition.c:85
int errcode(int sqlerrcode)
Definition: elog.c:575
Datum upper(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:74
char strategy
Definition: rel.h:54
signed int int32
Definition: c.h:256
PartitionBoundInfo boundinfo
Definition: partition.h:37
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
#define ERROR
Definition: elog.h:43
static int partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo, void *probe, bool probe_is_bound, bool *is_equal)
Definition: partition.c:2309
static int32 partition_rbound_cmp(PartitionKey key, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partition.c:2158
int errdetail(const char *fmt,...)
Definition: elog.c:873
#define ereport(elevel, rest)
Definition: elog.h:122
char * get_range_partbound_string(List *bound_datums)
Definition: ruleutils.c:10916
static PartitionRangeBound * make_one_range_bound(PartitionKey key, int index, List *datums, bool lower)
Definition: partition.c:2097
#define NULL
Definition: c.h:229
#define Assert(condition)
Definition: c.h:676
#define lfirst(lc)
Definition: pg_list.h:106
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:786
#define RelationGetPartitionKey(relation)
Definition: rel.h:584
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:787
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define elog
Definition: elog.h:219
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1726
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:2248
bool constisnull
Definition: primnodes.h:197
#define RelationGetPartitionDesc(relation)
Definition: rel.h:632
void FormPartitionKeyDatum ( PartitionDispatch  pd,
TupleTableSlot slot,
EState estate,
Datum values,
bool isnull 
)

Definition at line 1875 of file partition.c.

References Assert, elog, ERROR, ExecEvalExprSwitchContext(), ExecPrepareExprList(), GetPerTupleExprContext, i, PartitionDispatchData::key, PartitionDispatchData::keystate, lfirst, list_head(), lnext, NIL, NULL, PartitionKeyData::partattrs, PartitionKeyData::partexprs, PartitionKeyData::partnatts, and slot_getattr().

Referenced by ExecFindPartition(), and get_partition_for_tuple().

1880 {
1881  ListCell *partexpr_item;
1882  int i;
1883 
1884  if (pd->key->partexprs != NIL && pd->keystate == NIL)
1885  {
1886  /* Check caller has set up context correctly */
1887  Assert(estate != NULL &&
1888  GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
1889 
1890  /* First time through, set up expression evaluation state */
1891  pd->keystate = ExecPrepareExprList(pd->key->partexprs, estate);
1892  }
1893 
1894  partexpr_item = list_head(pd->keystate);
1895  for (i = 0; i < pd->key->partnatts; i++)
1896  {
1897  AttrNumber keycol = pd->key->partattrs[i];
1898  Datum datum;
1899  bool isNull;
1900 
1901  if (keycol != 0)
1902  {
1903  /* Plain column; get the value directly from the heap tuple */
1904  datum = slot_getattr(slot, keycol, &isNull);
1905  }
1906  else
1907  {
1908  /* Expression; need to evaluate it */
1909  if (partexpr_item == NULL)
1910  elog(ERROR, "wrong number of partition key expressions");
1911  datum = ExecEvalExprSwitchContext((ExprState *) lfirst(partexpr_item),
1912  GetPerTupleExprContext(estate),
1913  &isNull);
1914  partexpr_item = lnext(partexpr_item);
1915  }
1916  values[i] = datum;
1917  isnull[i] = isNull;
1918  }
1919 
1920  if (partexpr_item != NULL)
1921  elog(ERROR, "wrong number of partition key expressions");
1922 }
#define NIL
Definition: pg_list.h:69
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:300
List * partexprs
Definition: rel.h:58
#define GetPerTupleExprContext(estate)
Definition: executor.h:475
#define ERROR
Definition: elog.h:43
static ListCell * list_head(const List *l)
Definition: pg_list.h:77
#define lnext(lc)
Definition: pg_list.h:105
List * ExecPrepareExprList(List *nodes, EState *estate)
Definition: execExpr.c:511
AttrNumber * partattrs
Definition: rel.h:56
uintptr_t Datum
Definition: postgres.h:372
int16 partnatts
Definition: rel.h:55
#define NULL
Definition: c.h:229
#define Assert(condition)
Definition: c.h:676
#define lfirst(lc)
Definition: pg_list.h:106
static Datum values[MAXATTR]
Definition: bootstrap.c:163
int i
Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: heaptuple.c:1142
#define elog
Definition: elog.h:219
int16 AttrNumber
Definition: attnum.h:21
PartitionKey key
Definition: partition.h:63
static List * generate_partition_qual ( Relation  rel)
static

Definition at line 1788 of file partition.c.

References AccessShareLock, Anum_pg_class_relpartbound, CacheMemoryContext, castNode, check_stack_depth(), copyObject, elog, ERROR, get_partition_parent(), get_qual_from_partbound(), heap_close, heap_open(), HeapTupleIsValid, list_concat(), map_partition_varattnos(), MemoryContextSwitchTo(), NIL, NoLock, RelationData::rd_partcheck, RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RELOID, result, SearchSysCache1, stringToNode(), SysCacheGetAttr(), and TextDatumGetCString.

Referenced by get_partition_qual_relid(), and RelationGetPartitionQual().

1789 {
1790  HeapTuple tuple;
1791  MemoryContext oldcxt;
1792  Datum boundDatum;
1793  bool isnull;
1794  PartitionBoundSpec *bound;
1795  List *my_qual = NIL,
1796  *result = NIL;
1797  Relation parent;
1798  bool found_whole_row;
1799 
1800  /* Guard against stack overflow due to overly deep partition tree */
1802 
1803  /* Quick copy */
1804  if (rel->rd_partcheck != NIL)
1805  return copyObject(rel->rd_partcheck);
1806 
1807  /* Grab at least an AccessShareLock on the parent table */
1809  AccessShareLock);
1810 
1811  /* Get pg_class.relpartbound */
1812  tuple = SearchSysCache1(RELOID, RelationGetRelid(rel));
1813  if (!HeapTupleIsValid(tuple))
1814  elog(ERROR, "cache lookup failed for relation %u",
1815  RelationGetRelid(rel));
1816 
1817  boundDatum = SysCacheGetAttr(RELOID, tuple,
1819  &isnull);
1820  if (isnull) /* should not happen */
1821  elog(ERROR, "relation \"%s\" has relpartbound = null",
1823  bound = castNode(PartitionBoundSpec,
1824  stringToNode(TextDatumGetCString(boundDatum)));
1825  ReleaseSysCache(tuple);
1826 
1827  my_qual = get_qual_from_partbound(rel, parent, bound);
1828 
1829  /* Add the parent's quals to the list (if any) */
1830  if (parent->rd_rel->relispartition)
1831  result = list_concat(generate_partition_qual(parent), my_qual);
1832  else
1833  result = my_qual;
1834 
1835  /*
1836  * Change Vars to have partition's attnos instead of the parent's. We do
1837  * this after we concatenate the parent's quals, because we want every Var
1838  * in it to bear this relation's attnos. It's safe to assume varno = 1
1839  * here.
1840  */
1841  result = map_partition_varattnos(result, 1, rel, parent,
1842  &found_whole_row);
1843  /* There can never be a whole-row reference here */
1844  if (found_whole_row)
1845  elog(ERROR, "unexpected whole-row reference found in partition key");
1846 
1847  /* Save a copy in the relcache */
1849  rel->rd_partcheck = copyObject(result);
1850  MemoryContextSwitchTo(oldcxt);
1851 
1852  /* Keep the parent locked until commit */
1853  heap_close(parent, NoLock);
1854 
1855  return result;
1856 }
#define NIL
Definition: pg_list.h:69
void * stringToNode(char *str)
Definition: read.c:38
#define castNode(_type_, nodeptr)
Definition: nodes.h:578
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define AccessShareLock
Definition: lockdefs.h:36
List * list_concat(List *list1, List *list2)
Definition: list.c:321
return result
Definition: formatting.c:1633
#define heap_close(r, l)
Definition: heapam.h:97
Form_pg_class rd_rel
Definition: rel.h:114
List * get_qual_from_partbound(Relation rel, Relation parent, PartitionBoundSpec *spec)
Definition: partition.c:871
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:156
List * map_partition_varattnos(List *expr, int target_varno, Relation partrel, Relation parent, bool *found_whole_row)
Definition: partition.c:914
#define ERROR
Definition: elog.h:43
Oid get_partition_parent(Oid relid)
Definition: partition.c:829
#define NoLock
Definition: lockdefs.h:34
void check_stack_depth(void)
Definition: postgres.c:3117
#define RelationGetRelationName(relation)
Definition: rel.h:436
#define TextDatumGetCString(d)
Definition: builtins.h:92
uintptr_t Datum
Definition: postgres.h:372
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1117
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1279
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
#define Anum_pg_class_relpartbound
Definition: pg_class.h:135
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
static List * generate_partition_qual(Relation rel)
Definition: partition.c:1788
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:622
Definition: pg_list.h:45
#define RelationGetRelid(relation)
Definition: rel.h:416
List * rd_partcheck
Definition: rel.h:132
MemoryContext CacheMemoryContext
Definition: mcxt.c:46
int get_partition_for_tuple ( PartitionDispatch pd,
TupleTableSlot slot,
EState estate,
PartitionDispatchData **  failed_at,
TupleTableSlot **  failed_slot 
)

Definition at line 1934 of file partition.c.

References PartitionDescData::boundinfo, do_convert_tuple(), ExprContext::ecxt_scantuple, elog, equal(), ERROR, ExecClearTuple(), ExecFetchSlotTuple(), ExecStoreTuple(), FormPartitionKeyDatum(), GetPerTupleExprContext, i, PartitionDispatchData::indexes, PartitionBoundInfoData::indexes, InvalidBuffer, PartitionDispatchData::key, PartitionDescData::nparts, NULL, PartitionBoundInfoData::null_index, PartitionDispatchData::partdesc, partition_bound_accepts_nulls, partition_bound_bsearch(), PARTITION_MAX_KEYS, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionKeyData::partnatts, result, PartitionKeyData::strategy, PartitionDispatchData::tupmap, PartitionDispatchData::tupslot, and values.

Referenced by ExecFindPartition().

1939 {
1940  PartitionDispatch parent;
1942  bool isnull[PARTITION_MAX_KEYS];
1943  int cur_offset,
1944  cur_index;
1945  int i,
1946  result;
1947  ExprContext *ecxt = GetPerTupleExprContext(estate);
1948  TupleTableSlot *ecxt_scantuple_old = ecxt->ecxt_scantuple;
1949 
1950  /* start with the root partitioned table */
1951  parent = pd[0];
1952  while (true)
1953  {
1954  PartitionKey key = parent->key;
1955  PartitionDesc partdesc = parent->partdesc;
1956  TupleTableSlot *myslot = parent->tupslot;
1957  TupleConversionMap *map = parent->tupmap;
1958 
1959  if (myslot != NULL && map != NULL)
1960  {
1961  HeapTuple tuple = ExecFetchSlotTuple(slot);
1962 
1963  ExecClearTuple(myslot);
1964  tuple = do_convert_tuple(tuple, map);
1965  ExecStoreTuple(tuple, myslot, InvalidBuffer, true);
1966  slot = myslot;
1967  }
1968 
1969  /* Quick exit */
1970  if (partdesc->nparts == 0)
1971  {
1972  *failed_at = parent;
1973  *failed_slot = slot;
1974  result = -1;
1975  goto error_exit;
1976  }
1977 
1978  /*
1979  * Extract partition key from tuple. Expression evaluation machinery
1980  * that FormPartitionKeyDatum() invokes expects ecxt_scantuple to
1981  * point to the correct tuple slot. The slot might have changed from
1982  * what was used for the parent table if the table of the current
1983  * partitioning level has different tuple descriptor from the parent.
1984  * So update ecxt_scantuple accordingly.
1985  */
1986  ecxt->ecxt_scantuple = slot;
1987  FormPartitionKeyDatum(parent, slot, estate, values, isnull);
1988 
1989  if (key->strategy == PARTITION_STRATEGY_RANGE)
1990  {
1991  /*
1992  * Since we cannot route tuples with NULL partition keys through a
1993  * range-partitioned table, simply return that no partition exists
1994  */
1995  for (i = 0; i < key->partnatts; i++)
1996  {
1997  if (isnull[i])
1998  {
1999  *failed_at = parent;
2000  *failed_slot = slot;
2001  result = -1;
2002  goto error_exit;
2003  }
2004  }
2005  }
2006 
2007  /*
2008  * A null partition key is only acceptable if null-accepting list
2009  * partition exists.
2010  */
2011  cur_index = -1;
2012  if (isnull[0] && partition_bound_accepts_nulls(partdesc->boundinfo))
2013  cur_index = partdesc->boundinfo->null_index;
2014  else if (!isnull[0])
2015  {
2016  /* Else bsearch in partdesc->boundinfo */
2017  bool equal = false;
2018 
2019  cur_offset = partition_bound_bsearch(key, partdesc->boundinfo,
2020  values, false, &equal);
2021  switch (key->strategy)
2022  {
2024  if (cur_offset >= 0 && equal)
2025  cur_index = partdesc->boundinfo->indexes[cur_offset];
2026  else
2027  cur_index = -1;
2028  break;
2029 
2031 
2032  /*
2033  * Offset returned is such that the bound at offset is
2034  * found to be less or equal with the tuple. So, the bound
2035  * at offset+1 would be the upper bound.
2036  */
2037  cur_index = partdesc->boundinfo->indexes[cur_offset + 1];
2038  break;
2039 
2040  default:
2041  elog(ERROR, "unexpected partition strategy: %d",
2042  (int) key->strategy);
2043  }
2044  }
2045 
2046  /*
2047  * cur_index < 0 means we failed to find a partition of this parent.
2048  * cur_index >= 0 means we either found the leaf partition, or the
2049  * next parent to find a partition of.
2050  */
2051  if (cur_index < 0)
2052  {
2053  result = -1;
2054  *failed_at = parent;
2055  *failed_slot = slot;
2056  break;
2057  }
2058  else if (parent->indexes[cur_index] >= 0)
2059  {
2060  result = parent->indexes[cur_index];
2061  break;
2062  }
2063  else
2064  parent = pd[-parent->indexes[cur_index]];
2065  }
2066 
2067 error_exit:
2068  ecxt->ecxt_scantuple = ecxt_scantuple_old;
2069  return result;
2070 }
TupleTableSlot * ExecStoreTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree)
Definition: execTuples.c:320
PartitionDesc partdesc
Definition: partition.h:65
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2962
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:439
TupleConversionMap * tupmap
Definition: partition.h:67
#define InvalidBuffer
Definition: buf.h:25
#define partition_bound_accepts_nulls(bi)
Definition: partition.c:85
#define PARTITION_MAX_KEYS
return result
Definition: formatting.c:1633
char strategy
Definition: rel.h:54
PartitionBoundInfo boundinfo
Definition: partition.h:37
#define GetPerTupleExprContext(estate)
Definition: executor.h:475
#define ERROR
Definition: elog.h:43
static int partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo, void *probe, bool probe_is_bound, bool *is_equal)
Definition: partition.c:2309
void FormPartitionKeyDatum(PartitionDispatch pd, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
Definition: partition.c:1875
uintptr_t Datum
Definition: postgres.h:372
int16 partnatts
Definition: rel.h:55
TupleTableSlot * tupslot
Definition: partition.h:66
#define NULL
Definition: c.h:229
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:197
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:786
HeapTuple do_convert_tuple(HeapTuple tuple, TupleConversionMap *map)
Definition: tupconvert.c:354
HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot)
Definition: execTuples.c:618
static Datum values[MAXATTR]
Definition: bootstrap.c:163
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:787
int i
#define elog
Definition: elog.h:219
PartitionKey key
Definition: partition.h:63
static Oid get_partition_operator ( PartitionKey  key,
int  col,
StrategyNumber  strategy,
bool need_relabel 
)
static

Definition at line 1170 of file partition.c.

References elog, ERROR, get_opfamily_member(), OidIsValid, PartitionKeyData::partopcintype, PartitionKeyData::partopfamily, and PartitionKeyData::parttypid.

Referenced by make_partition_op_expr().

1172 {
1173  Oid operoid;
1174 
1175  /*
1176  * First check if there exists an operator of the given strategy, with
1177  * this column's type as both its lefttype and righttype, in the
1178  * partitioning operator family specified for the column.
1179  */
1180  operoid = get_opfamily_member(key->partopfamily[col],
1181  key->parttypid[col],
1182  key->parttypid[col],
1183  strategy);
1184 
1185  /*
1186  * If one doesn't exist, we must resort to using an operator in the same
1187  * operator family but with the operator class declared input type. It is
1188  * OK to do so, because the column's type is known to be binary-coercible
1189  * with the operator class input type (otherwise, the operator class in
1190  * question would not have been accepted as the partitioning operator
1191  * class). We must however inform the caller to wrap the non-Const
1192  * expression with a RelabelType node to denote the implicit coercion. It
1193  * ensures that the resulting expression structurally matches similarly
1194  * processed expressions within the optimizer.
1195  */
1196  if (!OidIsValid(operoid))
1197  {
1198  operoid = get_opfamily_member(key->partopfamily[col],
1199  key->partopcintype[col],
1200  key->partopcintype[col],
1201  strategy);
1202  if (!OidIsValid(operoid))
1203  elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
1204  strategy, key->partopcintype[col], key->partopcintype[col],
1205  key->partopfamily[col]);
1206  *need_relabel = true;
1207  }
1208  else
1209  *need_relabel = false;
1210 
1211  return operoid;
1212 }
Oid * partopfamily
Definition: rel.h:61
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:538
#define ERROR
Definition: elog.h:43
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:163
Oid * parttypid
Definition: rel.h:69
Oid * partopcintype
Definition: rel.h:62
#define elog
Definition: elog.h:219
Oid get_partition_parent ( Oid  relid)

Definition at line 829 of file partition.c.

References AccessShareLock, Anum_pg_inherits_inhrelid, Anum_pg_inherits_inhseqno, BTEqualStrategyNumber, elog, ERROR, GETSTRUCT, heap_close, heap_open(), HeapTupleIsValid, InheritsRelationId, InheritsRelidSeqnoIndexId, Int32GetDatum, NULL, ObjectIdGetDatum, result, ScanKeyInit(), systable_beginscan(), systable_endscan(), and systable_getnext().

Referenced by ATExecDropNotNull(), generate_partition_qual(), heap_drop_with_catalog(), and RangeVarCallbackForDropRelation().

830 {
831  Form_pg_inherits form;
832  Relation catalogRelation;
833  SysScanDesc scan;
834  ScanKeyData key[2];
835  HeapTuple tuple;
836  Oid result;
837 
838  catalogRelation = heap_open(InheritsRelationId, AccessShareLock);
839 
840  ScanKeyInit(&key[0],
842  BTEqualStrategyNumber, F_OIDEQ,
843  ObjectIdGetDatum(relid));
844  ScanKeyInit(&key[1],
846  BTEqualStrategyNumber, F_INT4EQ,
847  Int32GetDatum(1));
848 
849  scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId, true,
850  NULL, 2, key);
851 
852  tuple = systable_getnext(scan);
853  if (!HeapTupleIsValid(tuple))
854  elog(ERROR, "could not find tuple for parent of relation %u", relid);
855 
856  form = (Form_pg_inherits) GETSTRUCT(tuple);
857  result = form->inhparent;
858 
859  systable_endscan(scan);
860  heap_close(catalogRelation, AccessShareLock);
861 
862  return result;
863 }
#define Anum_pg_inherits_inhrelid
Definition: pg_inherits.h:50
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:499
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
#define AccessShareLock
Definition: lockdefs.h:36
return result
Definition: formatting.c:1633
#define heap_close(r, l)
Definition: heapam.h:97
unsigned int Oid
Definition: postgres_ext.h:31
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:328
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:416
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
#define ERROR
Definition: elog.h:43
#define Anum_pg_inherits_inhseqno
Definition: pg_inherits.h:52
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define NULL
Definition: c.h:229
#define InheritsRelidSeqnoIndexId
Definition: indexing.h:167
FormData_pg_inherits * Form_pg_inherits
Definition: pg_inherits.h:43
#define InheritsRelationId
Definition: pg_inherits.h:29
#define Int32GetDatum(X)
Definition: postgres.h:485
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define elog
Definition: elog.h:219
#define BTEqualStrategyNumber
Definition: stratnum.h:31
Expr* get_partition_qual_relid ( Oid  relid)

Definition at line 961 of file partition.c.

References AccessShareLock, AND_EXPR, generate_partition_qual(), heap_close, heap_open(), linitial, list_length(), makeBoolExpr(), NoLock, NULL, RelationData::rd_rel, and result.

Referenced by pg_get_partition_constraintdef().

962 {
963  Relation rel = heap_open(relid, AccessShareLock);
964  Expr *result = NULL;
965  List *and_args;
966 
967  /* Do the work only if this relation is a partition. */
968  if (rel->rd_rel->relispartition)
969  {
970  and_args = generate_partition_qual(rel);
971  if (list_length(and_args) > 1)
972  result = makeBoolExpr(AND_EXPR, and_args, -1);
973  else
974  result = linitial(and_args);
975  }
976 
977  /* Keep the lock. */
978  heap_close(rel, NoLock);
979 
980  return result;
981 }
#define AccessShareLock
Definition: lockdefs.h:36
return result
Definition: formatting.c:1633
#define heap_close(r, l)
Definition: heapam.h:97
Form_pg_class rd_rel
Definition: rel.h:114
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:366
#define linitial(l)
Definition: pg_list.h:111
#define NoLock
Definition: lockdefs.h:34
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
#define NULL
Definition: c.h:229
static int list_length(const List *l)
Definition: pg_list.h:89
static List * generate_partition_qual(Relation rel)
Definition: partition.c:1788
Definition: pg_list.h:45
static List * get_qual_for_list ( PartitionKey  key,
PartitionBoundSpec spec 
)
static

Definition at line 1288 of file partition.c.

References NullTest::arg, NullTest::argisrow, ArrayExpr::array_collid, ArrayExpr::array_typeid, Assert, BTEqualStrategyNumber, castNode, Const::constisnull, copyObject, ArrayExpr::element_typeid, ArrayExpr::elements, get_array_type(), IS_NOT_NULL, IS_NULL, lappend(), lfirst, linitial, list_make1, list_make2, PartitionBoundSpec::listdatums, ArrayExpr::location, NullTest::location, make_partition_op_expr(), makeBoolExpr(), makeNode, makeVar(), ArrayExpr::multidims, NIL, NULL, NullTest::nulltesttype, OR_EXPR, PartitionKeyData::partattrs, PartitionKeyData::partexprs, PartitionKeyData::partnatts, PartitionKeyData::parttypcoll, PartitionKeyData::parttypid, PartitionKeyData::parttypmod, result, type_is_array, and val.

Referenced by get_qual_from_partbound().

1289 {
1290  List *result;
1291  Expr *keyCol;
1292  ArrayExpr *arr;
1293  Expr *opexpr;
1294  NullTest *nulltest;
1295  ListCell *cell;
1296  List *arrelems = NIL;
1297  bool list_has_null = false;
1298 
1299  /*
1300  * Only single-column list partitioning is supported, so we are worried
1301  * only about the partition key with index 0.
1302  */
1303  Assert(key->partnatts == 1);
1304 
1305  /* Construct Var or expression representing the partition column */
1306  if (key->partattrs[0] != 0)
1307  keyCol = (Expr *) makeVar(1,
1308  key->partattrs[0],
1309  key->parttypid[0],
1310  key->parttypmod[0],
1311  key->parttypcoll[0],
1312  0);
1313  else
1314  keyCol = (Expr *) copyObject(linitial(key->partexprs));
1315 
1316  /* Create list of Consts for the allowed values, excluding any nulls */
1317  foreach(cell, spec->listdatums)
1318  {
1319  Const *val = castNode(Const, lfirst(cell));
1320 
1321  if (val->constisnull)
1322  list_has_null = true;
1323  else
1324  arrelems = lappend(arrelems, copyObject(val));
1325  }
1326 
1327  if (arrelems)
1328  {
1329  /* Construct an ArrayExpr for the non-null partition values */
1330  arr = makeNode(ArrayExpr);
1331  arr->array_typeid = !type_is_array(key->parttypid[0])
1332  ? get_array_type(key->parttypid[0])
1333  : key->parttypid[0];
1334  arr->array_collid = key->parttypcoll[0];
1335  arr->element_typeid = key->parttypid[0];
1336  arr->elements = arrelems;
1337  arr->multidims = false;
1338  arr->location = -1;
1339 
1340  /* Generate the main expression, i.e., keyCol = ANY (arr) */
1342  keyCol, (Expr *) arr);
1343  }
1344  else
1345  {
1346  /* If there are no partition values, we don't need an = ANY expr */
1347  opexpr = NULL;
1348  }
1349 
1350  if (!list_has_null)
1351  {
1352  /*
1353  * Gin up a "col IS NOT NULL" test that will be AND'd with the main
1354  * expression. This might seem redundant, but the partition routing
1355  * machinery needs it.
1356  */
1357  nulltest = makeNode(NullTest);
1358  nulltest->arg = keyCol;
1359  nulltest->nulltesttype = IS_NOT_NULL;
1360  nulltest->argisrow = false;
1361  nulltest->location = -1;
1362 
1363  result = opexpr ? list_make2(nulltest, opexpr) : list_make1(nulltest);
1364  }
1365  else
1366  {
1367  /*
1368  * Gin up a "col IS NULL" test that will be OR'd with the main
1369  * expression.
1370  */
1371  nulltest = makeNode(NullTest);
1372  nulltest->arg = keyCol;
1373  nulltest->nulltesttype = IS_NULL;
1374  nulltest->argisrow = false;
1375  nulltest->location = -1;
1376 
1377  if (opexpr)
1378  {
1379  Expr *or;
1380 
1381  or = makeBoolExpr(OR_EXPR, list_make2(nulltest, opexpr), -1);
1382  result = list_make1(or);
1383  }
1384  else
1385  result = list_make1(nulltest);
1386  }
1387 
1388  return result;
1389 }
#define list_make2(x1, x2)
Definition: pg_list.h:140
bool multidims
Definition: primnodes.h:956
#define NIL
Definition: pg_list.h:69
static Expr * make_partition_op_expr(PartitionKey key, int keynum, uint16 strategy, Expr *arg1, Expr *arg2)
Definition: partition.c:1220
#define castNode(_type_, nodeptr)
Definition: nodes.h:578
Oid get_array_type(Oid typid)
Definition: lsyscache.c:2512
Oid array_typeid
Definition: primnodes.h:952
return result
Definition: formatting.c:1633
List * partexprs
Definition: rel.h:58
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:366
#define list_make1(x1)
Definition: pg_list.h:139
Oid * parttypcoll
Definition: rel.h:74
#define linitial(l)
Definition: pg_list.h:111
Expr * arg
Definition: primnodes.h:1180
List * elements
Definition: primnodes.h:955
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:67
Oid * parttypid
Definition: rel.h:69
List * lappend(List *list, void *datum)
Definition: list.c:128
int location
Definition: primnodes.h:957
AttrNumber * partattrs
Definition: rel.h:56
int16 partnatts
Definition: rel.h:55
NullTestType nulltesttype
Definition: primnodes.h:1181
int32 * parttypmod
Definition: rel.h:70
#define makeNode(_type_)
Definition: nodes.h:557
#define NULL
Definition: c.h:229
#define Assert(condition)
Definition: c.h:676
#define lfirst(lc)
Definition: pg_list.h:106
Oid array_collid
Definition: primnodes.h:953
int location
Definition: primnodes.h:1183
#define type_is_array(typid)
Definition: lsyscache.h:180
Oid element_typeid
Definition: primnodes.h:954
bool argisrow
Definition: primnodes.h:1182
#define copyObject(obj)
Definition: nodes.h:622
Definition: pg_list.h:45
long val
Definition: informix.c:689
bool constisnull
Definition: primnodes.h:197
#define BTEqualStrategyNumber
Definition: stratnum.h:31
static List * get_qual_for_range ( PartitionKey  key,
PartitionBoundSpec spec 
)
static

Definition at line 1486 of file partition.c.

References AND_EXPR, NullTest::arg, NullTest::argisrow, BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, castNode, copyObject, CreateExecutorState(), DatumGetBool, elog, ERROR, EState::es_query_cxt, ExecEvalExprSwitchContext(), ExecInitExpr(), fix_opfuncids(), for_both_cell, forboth, FreeExecutorState(), get_range_key_properties(), GetPerTupleExprContext, i, IS_NOT_NULL, PartitionRangeDatum::kind, lappend(), lfirst, linitial, list_head(), list_length(), list_make1, lnext, NullTest::location, PartitionBoundSpec::lowerdatums, make_partition_op_expr(), makeBoolConst(), makeBoolExpr(), makeNode, makeVar(), MemoryContextSwitchTo(), NIL, NULL, NullTest::nulltesttype, OR_EXPR, PartitionKeyData::partattrs, PartitionKeyData::partexprs, PARTITION_RANGE_DATUM_MAXVALUE, PARTITION_RANGE_DATUM_MINVALUE, PARTITION_RANGE_DATUM_VALUE, PartitionKeyData::partnatts, PartitionKeyData::parttypcoll, PartitionKeyData::parttypid, PartitionKeyData::parttypmod, result, and PartitionBoundSpec::upperdatums.

Referenced by get_qual_from_partbound().

1487 {
1488  List *result = NIL;
1489  ListCell *cell1,
1490  *cell2,
1491  *partexprs_item,
1492  *partexprs_item_saved;
1493  int i,
1494  j;
1495  PartitionRangeDatum *ldatum,
1496  *udatum;
1497  Expr *keyCol;
1498  Const *lower_val,
1499  *upper_val;
1500  NullTest *nulltest;
1501  List *lower_or_arms,
1502  *upper_or_arms;
1503  int num_or_arms,
1504  current_or_arm;
1505  ListCell *lower_or_start_datum,
1506  *upper_or_start_datum;
1507  bool need_next_lower_arm,
1508  need_next_upper_arm;
1509 
1510  lower_or_start_datum = list_head(spec->lowerdatums);
1511  upper_or_start_datum = list_head(spec->upperdatums);
1512  num_or_arms = key->partnatts;
1513 
1514  /*
1515  * A range-partitioned table does not currently allow partition keys to be
1516  * null, so emit an IS NOT NULL expression for each key column.
1517  */
1518  partexprs_item = list_head(key->partexprs);
1519  for (i = 0; i < key->partnatts; i++)
1520  {
1521  Expr *keyCol;
1522 
1523  if (key->partattrs[i] != 0)
1524  {
1525  keyCol = (Expr *) makeVar(1,
1526  key->partattrs[i],
1527  key->parttypid[i],
1528  key->parttypmod[i],
1529  key->parttypcoll[i],
1530  0);
1531  }
1532  else
1533  {
1534  if (partexprs_item == NULL)
1535  elog(ERROR, "wrong number of partition key expressions");
1536  keyCol = copyObject(lfirst(partexprs_item));
1537  partexprs_item = lnext(partexprs_item);
1538  }
1539 
1540  nulltest = makeNode(NullTest);
1541  nulltest->arg = keyCol;
1542  nulltest->nulltesttype = IS_NOT_NULL;
1543  nulltest->argisrow = false;
1544  nulltest->location = -1;
1545  result = lappend(result, nulltest);
1546  }
1547 
1548  /*
1549  * Iterate over the key columns and check if the corresponding lower and
1550  * upper datums are equal using the btree equality operator for the
1551  * column's type. If equal, we emit single keyCol = common_value
1552  * expression. Starting from the first column for which the corresponding
1553  * lower and upper bound datums are not equal, we generate OR expressions
1554  * as shown in the function's header comment.
1555  */
1556  i = 0;
1557  partexprs_item = list_head(key->partexprs);
1558  partexprs_item_saved = partexprs_item; /* placate compiler */
1559  forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
1560  {
1561  EState *estate;
1562  MemoryContext oldcxt;
1563  Expr *test_expr;
1564  ExprState *test_exprstate;
1565  Datum test_result;
1566  bool isNull;
1567 
1568  ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
1569  udatum = castNode(PartitionRangeDatum, lfirst(cell2));
1570 
1571  /*
1572  * Since get_range_key_properties() modifies partexprs_item, and we
1573  * might need to start over from the previous expression in the later
1574  * part of this function, save away the current value.
1575  */
1576  partexprs_item_saved = partexprs_item;
1577 
1578  get_range_key_properties(key, i, ldatum, udatum,
1579  &partexprs_item,
1580  &keyCol,
1581  &lower_val, &upper_val);
1582 
1583  /*
1584  * If either value is NULL, the corresponding partition bound is
1585  * either MINVALUE or MAXVALUE, and we treat them as unequal, because
1586  * even if they're the same, there is no common value to equate the
1587  * key column with.
1588  */
1589  if (!lower_val || !upper_val)
1590  break;
1591 
1592  /* Create the test expression */
1593  estate = CreateExecutorState();
1594  oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
1595  test_expr = make_partition_op_expr(key, i, BTEqualStrategyNumber,
1596  (Expr *) lower_val,
1597  (Expr *) upper_val);
1598  fix_opfuncids((Node *) test_expr);
1599  test_exprstate = ExecInitExpr(test_expr, NULL);
1600  test_result = ExecEvalExprSwitchContext(test_exprstate,
1601  GetPerTupleExprContext(estate),
1602  &isNull);
1603  MemoryContextSwitchTo(oldcxt);
1604  FreeExecutorState(estate);
1605 
1606  /* If not equal, go generate the OR expressions */
1607  if (!DatumGetBool(test_result))
1608  break;
1609 
1610  /*
1611  * The bounds for the last key column can't be equal, because such a
1612  * range partition would never be allowed to be defined (it would have
1613  * an empty range otherwise).
1614  */
1615  if (i == key->partnatts - 1)
1616  elog(ERROR, "invalid range bound specification");
1617 
1618  /* Equal, so generate keyCol = lower_val expression */
1619  result = lappend(result,
1621  keyCol, (Expr *) lower_val));
1622 
1623  i++;
1624  }
1625 
1626  /* First pair of lower_val and upper_val that are not equal. */
1627  lower_or_start_datum = cell1;
1628  upper_or_start_datum = cell2;
1629 
1630  /* OR will have as many arms as there are key columns left. */
1631  num_or_arms = key->partnatts - i;
1632  current_or_arm = 0;
1633  lower_or_arms = upper_or_arms = NIL;
1634  need_next_lower_arm = need_next_upper_arm = true;
1635  while (current_or_arm < num_or_arms)
1636  {
1637  List *lower_or_arm_args = NIL,
1638  *upper_or_arm_args = NIL;
1639 
1640  /* Restart scan of columns from the i'th one */
1641  j = i;
1642  partexprs_item = partexprs_item_saved;
1643 
1644  for_both_cell(cell1, lower_or_start_datum, cell2, upper_or_start_datum)
1645  {
1646  PartitionRangeDatum *ldatum_next = NULL,
1647  *udatum_next = NULL;
1648 
1649  ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
1650  if (lnext(cell1))
1651  ldatum_next = castNode(PartitionRangeDatum,
1652  lfirst(lnext(cell1)));
1653  udatum = castNode(PartitionRangeDatum, lfirst(cell2));
1654  if (lnext(cell2))
1655  udatum_next = castNode(PartitionRangeDatum,
1656  lfirst(lnext(cell2)));
1657  get_range_key_properties(key, j, ldatum, udatum,
1658  &partexprs_item,
1659  &keyCol,
1660  &lower_val, &upper_val);
1661 
1662  if (need_next_lower_arm && lower_val)
1663  {
1664  uint16 strategy;
1665 
1666  /*
1667  * For the non-last columns of this arm, use the EQ operator.
1668  * For the last column of this arm, use GT, unless this is the
1669  * last column of the whole bound check, or the next bound
1670  * datum is MINVALUE, in which case use GE.
1671  */
1672  if (j - i < current_or_arm)
1673  strategy = BTEqualStrategyNumber;
1674  else if (j == key->partnatts - 1 ||
1675  (ldatum_next &&
1676  ldatum_next->kind == PARTITION_RANGE_DATUM_MINVALUE))
1677  strategy = BTGreaterEqualStrategyNumber;
1678  else
1679  strategy = BTGreaterStrategyNumber;
1680 
1681  lower_or_arm_args = lappend(lower_or_arm_args,
1682  make_partition_op_expr(key, j,
1683  strategy,
1684  keyCol,
1685  (Expr *) lower_val));
1686  }
1687 
1688  if (need_next_upper_arm && upper_val)
1689  {
1690  uint16 strategy;
1691 
1692  /*
1693  * For the non-last columns of this arm, use the EQ operator.
1694  * For the last column of this arm, use LT, unless the next
1695  * bound datum is MAXVALUE, in which case use LE.
1696  */
1697  if (j - i < current_or_arm)
1698  strategy = BTEqualStrategyNumber;
1699  else if (udatum_next &&
1700  udatum_next->kind == PARTITION_RANGE_DATUM_MAXVALUE)
1701  strategy = BTLessEqualStrategyNumber;
1702  else
1703  strategy = BTLessStrategyNumber;
1704 
1705  upper_or_arm_args = lappend(upper_or_arm_args,
1706  make_partition_op_expr(key, j,
1707  strategy,
1708  keyCol,
1709  (Expr *) upper_val));
1710  }
1711 
1712  /*
1713  * Did we generate enough of OR's arguments? First arm considers
1714  * the first of the remaining columns, second arm considers first
1715  * two of the remaining columns, and so on.
1716  */
1717  ++j;
1718  if (j - i > current_or_arm)
1719  {
1720  /*
1721  * We must not emit any more arms if the new column that will
1722  * be considered is unbounded, or this one was.
1723  */
1724  if (!lower_val || !ldatum_next ||
1725  ldatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
1726  need_next_lower_arm = false;
1727  if (!upper_val || !udatum_next ||
1728  udatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
1729  need_next_upper_arm = false;
1730  break;
1731  }
1732  }
1733 
1734  if (lower_or_arm_args != NIL)
1735  lower_or_arms = lappend(lower_or_arms,
1736  list_length(lower_or_arm_args) > 1
1737  ? makeBoolExpr(AND_EXPR, lower_or_arm_args, -1)
1738  : linitial(lower_or_arm_args));
1739 
1740  if (upper_or_arm_args != NIL)
1741  upper_or_arms = lappend(upper_or_arms,
1742  list_length(upper_or_arm_args) > 1
1743  ? makeBoolExpr(AND_EXPR, upper_or_arm_args, -1)
1744  : linitial(upper_or_arm_args));
1745 
1746  /* If no work to do in the next iteration, break away. */
1747  if (!need_next_lower_arm && !need_next_upper_arm)
1748  break;
1749 
1750  ++current_or_arm;
1751  }
1752 
1753  /*
1754  * Generate the OR expressions for each of lower and upper bounds (if
1755  * required), and append to the list of implicitly ANDed list of
1756  * expressions.
1757  */
1758  if (lower_or_arms != NIL)
1759  result = lappend(result,
1760  list_length(lower_or_arms) > 1
1761  ? makeBoolExpr(OR_EXPR, lower_or_arms, -1)
1762  : linitial(lower_or_arms));
1763  if (upper_or_arms != NIL)
1764  result = lappend(result,
1765  list_length(upper_or_arms) > 1
1766  ? makeBoolExpr(OR_EXPR, upper_or_arms, -1)
1767  : linitial(upper_or_arms));
1768 
1769  /* As noted above, caller expects the list to be non-empty. */
1770  if (result == NIL)
1771  result = list_make1(makeBoolConst(true, false));
1772 
1773  return result;
1774 }
#define NIL
Definition: pg_list.h:69
static Expr * make_partition_op_expr(PartitionKey key, int keynum, uint16 strategy, Expr *arg1, Expr *arg2)
Definition: partition.c:1220
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:300
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:180
#define castNode(_type_, nodeptr)
Definition: nodes.h:578
void fix_opfuncids(Node *node)
Definition: nodeFuncs.c:1582
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
PartitionRangeDatumKind kind
Definition: parsenodes.h:827
Definition: nodes.h:509
return result
Definition: formatting.c:1633
List * partexprs
Definition: rel.h:58
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:366
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
#define list_make1(x1)
Definition: pg_list.h:139
void FreeExecutorState(EState *estate)
Definition: execUtils.c:183
#define GetPerTupleExprContext(estate)
Definition: executor.h:475
unsigned short uint16
Definition: c.h:267
MemoryContext es_query_cxt
Definition: execnodes.h:471
Oid * parttypcoll
Definition: rel.h:74
#define linitial(l)
Definition: pg_list.h:111
#define ERROR
Definition: elog.h:43
Node * makeBoolConst(bool value, bool isnull)
Definition: makefuncs.c:354
Expr * arg
Definition: primnodes.h:1180
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:1407
#define DatumGetBool(X)
Definition: postgres.h:399
static ListCell * list_head(const List *l)
Definition: pg_list.h:77
#define lnext(lc)
Definition: pg_list.h:105
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:67
Oid * parttypid
Definition: rel.h:69
EState * CreateExecutorState(void)
Definition: execUtils.c:80
List * lappend(List *list, void *datum)
Definition: list.c:128
AttrNumber * partattrs
Definition: rel.h:56
uintptr_t Datum
Definition: postgres.h:372
int16 partnatts
Definition: rel.h:55
NullTestType nulltesttype
Definition: primnodes.h:1181
int32 * parttypmod
Definition: rel.h:70
#define for_both_cell(cell1, initcell1, cell2, initcell2)
Definition: pg_list.h:194
#define makeNode(_type_)
Definition: nodes.h:557
#define NULL
Definition: c.h:229
#define lfirst(lc)
Definition: pg_list.h:106
int location
Definition: primnodes.h:1183
static int list_length(const List *l)
Definition: pg_list.h:89
int i
bool argisrow
Definition: primnodes.h:1182
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:113
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:622
#define BTLessStrategyNumber
Definition: stratnum.h:29
Definition: pg_list.h:45
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32
List* get_qual_from_partbound ( Relation  rel,
Relation  parent,
PartitionBoundSpec spec 
)

Definition at line 871 of file partition.c.

References Assert, elog, ERROR, get_qual_for_list(), get_qual_for_range(), NIL, NULL, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, RelationGetPartitionKey, PartitionKeyData::strategy, and PartitionBoundSpec::strategy.

Referenced by ATExecAttachPartition(), and generate_partition_qual().

873 {
875  List *my_qual = NIL;
876 
877  Assert(key != NULL);
878 
879  switch (key->strategy)
880  {
883  my_qual = get_qual_for_list(key, spec);
884  break;
885 
888  my_qual = get_qual_for_range(key, spec);
889  break;
890 
891  default:
892  elog(ERROR, "unexpected partition strategy: %d",
893  (int) key->strategy);
894  }
895 
896  return my_qual;
897 }
#define NIL
Definition: pg_list.h:69
char strategy
Definition: rel.h:54
static List * get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
Definition: partition.c:1486
#define ERROR
Definition: elog.h:43
#define NULL
Definition: c.h:229
#define Assert(condition)
Definition: c.h:676
static List * get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
Definition: partition.c:1288
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:786
#define RelationGetPartitionKey(relation)
Definition: rel.h:584
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:787
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
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 
)
static

Definition at line 1407 of file partition.c.

References castNode, copyObject, elog, ERROR, PartitionRangeDatum::kind, lfirst, lnext, makeVar(), NULL, PartitionKeyData::partattrs, PARTITION_RANGE_DATUM_VALUE, PartitionKeyData::parttypcoll, PartitionKeyData::parttypid, PartitionKeyData::parttypmod, and PartitionRangeDatum::value.

Referenced by get_qual_for_range().

1413 {
1414  /* Get partition key expression for this column */
1415  if (key->partattrs[keynum] != 0)
1416  {
1417  *keyCol = (Expr *) makeVar(1,
1418  key->partattrs[keynum],
1419  key->parttypid[keynum],
1420  key->parttypmod[keynum],
1421  key->parttypcoll[keynum],
1422  0);
1423  }
1424  else
1425  {
1426  if (*partexprs_item == NULL)
1427  elog(ERROR, "wrong number of partition key expressions");
1428  *keyCol = copyObject(lfirst(*partexprs_item));
1429  *partexprs_item = lnext(*partexprs_item);
1430  }
1431 
1432  /* Get appropriate Const nodes for the bounds */
1433  if (ldatum->kind == PARTITION_RANGE_DATUM_VALUE)
1434  *lower_val = castNode(Const, copyObject(ldatum->value));
1435  else
1436  *lower_val = NULL;
1437 
1438  if (udatum->kind == PARTITION_RANGE_DATUM_VALUE)
1439  *upper_val = castNode(Const, copyObject(udatum->value));
1440  else
1441  *upper_val = NULL;
1442 }
#define castNode(_type_, nodeptr)
Definition: nodes.h:578
PartitionRangeDatumKind kind
Definition: parsenodes.h:827
Oid * parttypcoll
Definition: rel.h:74
#define ERROR
Definition: elog.h:43
#define lnext(lc)
Definition: pg_list.h:105
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:67
Oid * parttypid
Definition: rel.h:69
AttrNumber * partattrs
Definition: rel.h:56
int32 * parttypmod
Definition: rel.h:70
#define NULL
Definition: c.h:229
#define lfirst(lc)
Definition: pg_list.h:106
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:622
static PartitionRangeBound * make_one_range_bound ( PartitionKey  key,
int  index,
List datums,
bool  lower 
)
static

Definition at line 2097 of file partition.c.

References castNode, Const::constisnull, Const::constvalue, PartitionRangeBound::datums, elog, ERROR, i, PartitionRangeBound::index, PartitionRangeBound::kind, PartitionRangeDatum::kind, lfirst, lower(), PartitionRangeBound::lower, palloc0(), PARTITION_RANGE_DATUM_VALUE, PartitionKeyData::partnatts, val, and PartitionRangeDatum::value.

Referenced by check_new_partition_bound(), and RelationBuildPartitionDesc().

2098 {
2099  PartitionRangeBound *bound;
2100  ListCell *lc;
2101  int i;
2102 
2103  bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
2104  bound->index = index;
2105  bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
2106  bound->kind = (PartitionRangeDatumKind *) palloc0(key->partnatts *
2107  sizeof(PartitionRangeDatumKind));
2108  bound->lower = lower;
2109 
2110  i = 0;
2111  foreach(lc, datums)
2112  {
2114 
2115  /* What's contained in this range datum? */
2116  bound->kind[i] = datum->kind;
2117 
2118  if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
2119  {
2120  Const *val = castNode(Const, datum->value);
2121 
2122  if (val->constisnull)
2123  elog(ERROR, "invalid range bound datum");
2124  bound->datums[i] = val->constvalue;
2125  }
2126 
2127  i++;
2128  }
2129 
2130  return bound;
2131 }
Datum constvalue
Definition: primnodes.h:196
PartitionRangeDatumKind * kind
Definition: partition.c:104
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:43
#define castNode(_type_, nodeptr)
Definition: nodes.h:578
PartitionRangeDatumKind
Definition: parsenodes.h:816
PartitionRangeDatumKind kind
Definition: parsenodes.h:827
Definition: type.h:89
#define ERROR
Definition: elog.h:43
void * palloc0(Size size)
Definition: mcxt.c:878
uintptr_t Datum
Definition: postgres.h:372
int16 partnatts
Definition: rel.h:55
#define lfirst(lc)
Definition: pg_list.h:106
int i
#define elog
Definition: elog.h:219
long val
Definition: informix.c:689
bool constisnull
Definition: primnodes.h:197
static Expr * make_partition_op_expr ( PartitionKey  key,
int  keynum,
uint16  strategy,
Expr arg1,
Expr arg2 
)
static

Definition at line 1220 of file partition.c.

References ScalarArrayOpExpr::args, BOOLOID, COERCE_EXPLICIT_CAST, elog, ERROR, get_opcode(), get_partition_operator(), ScalarArrayOpExpr::inputcollid, InvalidOid, IsA, list_make2, ScalarArrayOpExpr::location, make_opclause(), makeNode, makeRelabelType(), NULL, ScalarArrayOpExpr::opfuncid, ScalarArrayOpExpr::opno, PartitionKeyData::partcollation, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionKeyData::partopcintype, PartitionKeyData::parttypcoll, result, PartitionKeyData::strategy, and ScalarArrayOpExpr::useOr.

Referenced by get_qual_for_list(), and get_qual_for_range().

1222 {
1223  Oid operoid;
1224  bool need_relabel = false;
1225  Expr *result = NULL;
1226 
1227  /* Get the correct btree operator for this partitioning column */
1228  operoid = get_partition_operator(key, keynum, strategy, &need_relabel);
1229 
1230  /*
1231  * Chosen operator may be such that the non-Const operand needs to be
1232  * coerced, so apply the same; see the comment in
1233  * get_partition_operator().
1234  */
1235  if (!IsA(arg1, Const) &&
1236  (need_relabel ||
1237  key->partcollation[keynum] != key->parttypcoll[keynum]))
1238  arg1 = (Expr *) makeRelabelType(arg1,
1239  key->partopcintype[keynum],
1240  -1,
1241  key->partcollation[keynum],
1243 
1244  /* Generate the actual expression */
1245  switch (key->strategy)
1246  {
1248  {
1249  ScalarArrayOpExpr *saopexpr;
1250 
1251  /* Build leftop = ANY (rightop) */
1252  saopexpr = makeNode(ScalarArrayOpExpr);
1253  saopexpr->opno = operoid;
1254  saopexpr->opfuncid = get_opcode(operoid);
1255  saopexpr->useOr = true;
1256  saopexpr->inputcollid = key->partcollation[keynum];
1257  saopexpr->args = list_make2(arg1, arg2);
1258  saopexpr->location = -1;
1259 
1260  result = (Expr *) saopexpr;
1261  break;
1262  }
1263 
1265  result = make_opclause(operoid,
1266  BOOLOID,
1267  false,
1268  arg1, arg2,
1269  InvalidOid,
1270  key->partcollation[keynum]);
1271  break;
1272 
1273  default:
1274  elog(ERROR, "invalid partitioning strategy");
1275  break;
1276  }
1277 
1278  return result;
1279 }
#define list_make2(x1, x2)
Definition: pg_list.h:140
#define IsA(nodeptr, _type_)
Definition: nodes.h:560
static Oid get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel)
Definition: partition.c:1170
return result
Definition: formatting.c:1633
Expr * make_opclause(Oid opno, Oid opresulttype, bool opretset, Expr *leftop, Expr *rightop, Oid opcollid, Oid inputcollid)
Definition: clauses.c:172
char strategy
Definition: rel.h:54
unsigned int Oid
Definition: postgres_ext.h:31
Oid * parttypcoll
Definition: rel.h:74
#define ERROR
Definition: elog.h:43
RelabelType * makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod, Oid rcollid, CoercionForm rformat)
Definition: makefuncs.c:399
Oid * partcollation
Definition: rel.h:66
#define InvalidOid
Definition: postgres_ext.h:36
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1094
#define makeNode(_type_)
Definition: nodes.h:557
#define NULL
Definition: c.h:229
#define BOOLOID
Definition: pg_type.h:288
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:786
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:787
Oid * partopcintype
Definition: rel.h:62
#define elog
Definition: elog.h:219
List* map_partition_varattnos ( List expr,
int  target_varno,
Relation  partrel,
Relation  parent,
bool found_whole_row 
)

Definition at line 914 of file partition.c.

References convert_tuples_by_name_map(), gettext_noop, map_variable_attnos(), NIL, RelationGetDescr, and RelationGetForm.

Referenced by ATExecAttachPartition(), ExecInitModifyTable(), generate_partition_qual(), and ValidatePartitionConstraints().

917 {
918  AttrNumber *part_attnos;
919  bool my_found_whole_row;
920 
921  if (expr == NIL)
922  return NIL;
923 
924  part_attnos = convert_tuples_by_name_map(RelationGetDescr(partrel),
925  RelationGetDescr(parent),
926  gettext_noop("could not convert row type"));
927  expr = (List *) map_variable_attnos((Node *) expr,
928  target_varno, 0,
929  part_attnos,
930  RelationGetDescr(parent)->natts,
931  RelationGetForm(partrel)->reltype,
932  &my_found_whole_row);
933  if (found_whole_row)
934  *found_whole_row = my_found_whole_row;
935 
936  return expr;
937 }
#define NIL
Definition: pg_list.h:69
#define RelationGetDescr(relation)
Definition: rel.h:428
#define RelationGetForm(relation)
Definition: rel.h:410
#define gettext_noop(x)
Definition: c.h:139
Definition: nodes.h:509
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)
AttrNumber * convert_tuples_by_name_map(TupleDesc indesc, TupleDesc outdesc, const char *msg)
Definition: tupconvert.c:293
Definition: pg_list.h:45
int16 AttrNumber
Definition: attnum.h:21
static int partition_bound_bsearch ( PartitionKey  key,
PartitionBoundInfo  boundinfo,
void *  probe,
bool  probe_is_bound,
bool is_equal 
)
static

Definition at line 2309 of file partition.c.

References PartitionBoundInfoData::ndatums, and partition_bound_cmp().

Referenced by check_new_partition_bound(), and get_partition_for_tuple().

2311 {
2312  int lo,
2313  hi,
2314  mid;
2315 
2316  lo = -1;
2317  hi = boundinfo->ndatums - 1;
2318  while (lo < hi)
2319  {
2320  int32 cmpval;
2321 
2322  mid = (lo + hi + 1) / 2;
2323  cmpval = partition_bound_cmp(key, boundinfo, mid, probe,
2324  probe_is_bound);
2325  if (cmpval <= 0)
2326  {
2327  lo = mid;
2328  *is_equal = (cmpval == 0);
2329 
2330  if (*is_equal)
2331  break;
2332  }
2333  else
2334  hi = mid - 1;
2335  }
2336 
2337  return lo;
2338 }
signed int int32
Definition: c.h:256
static int32 partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo, int offset, void *probe, bool probe_is_bound)
Definition: partition.c:2248
static int32 partition_bound_cmp ( PartitionKey  key,
PartitionBoundInfo  boundinfo,
int  offset,
void *  probe,
bool  probe_is_bound 
)
static

Definition at line 2248 of file partition.c.

References DatumGetInt32, PartitionBoundInfoData::datums, elog, ERROR, FunctionCall2Coll(), PartitionBoundInfoData::indexes, PartitionBoundInfoData::kind, lower(), PartitionKeyData::partcollation, partition_rbound_cmp(), partition_rbound_datum_cmp(), PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionKeyData::partsupfunc, and PartitionKeyData::strategy.

Referenced by check_new_partition_bound(), and partition_bound_bsearch().

2250 {
2251  Datum *bound_datums = boundinfo->datums[offset];
2252  int32 cmpval = -1;
2253 
2254  switch (key->strategy)
2255  {
2257  cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
2258  key->partcollation[0],
2259  bound_datums[0],
2260  *(Datum *) probe));
2261  break;
2262 
2264  {
2265  PartitionRangeDatumKind *kind = boundinfo->kind[offset];
2266 
2267  if (probe_is_bound)
2268  {
2269  /*
2270  * We need to pass whether the existing bound is a lower
2271  * bound, so that two equal-valued lower and upper bounds
2272  * are not regarded equal.
2273  */
2274  bool lower = boundinfo->indexes[offset] < 0;
2275 
2276  cmpval = partition_rbound_cmp(key,
2277  bound_datums, kind, lower,
2278  (PartitionRangeBound *) probe);
2279  }
2280  else
2281  cmpval = partition_rbound_datum_cmp(key,
2282  bound_datums, kind,
2283  (Datum *) probe);
2284  break;
2285  }
2286 
2287  default:
2288  elog(ERROR, "unexpected partition strategy: %d",
2289  (int) key->strategy);
2290  }
2291 
2292  return cmpval;
2293 }
PartitionRangeDatumKind ** kind
Definition: partition.c:76
#define DatumGetInt32(X)
Definition: postgres.h:478
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:43
FmgrInfo * partsupfunc
Definition: rel.h:63
PartitionRangeDatumKind
Definition: parsenodes.h:816
char strategy
Definition: rel.h:54
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1047
signed int int32
Definition: c.h:256
#define ERROR
Definition: elog.h:43
static int32 partition_rbound_cmp(PartitionKey key, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partition.c:2158
static int32 partition_rbound_datum_cmp(PartitionKey key, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *tuple_datums)
Definition: partition.c:2216
Oid * partcollation
Definition: rel.h:66
uintptr_t Datum
Definition: postgres.h:372
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:786
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:787
#define elog
Definition: elog.h:219
bool partition_bounds_equal ( int  partnatts,
int16 parttyplen,
bool parttypbyval,
PartitionBoundInfo  b1,
PartitionBoundInfo  b2 
)

Definition at line 586 of file partition.c.

References datumIsEqual(), PartitionBoundInfoData::datums, i, PartitionBoundInfoData::indexes, PartitionBoundInfoData::kind, PartitionBoundInfoData::ndatums, NULL, PartitionBoundInfoData::null_index, PARTITION_RANGE_DATUM_VALUE, PARTITION_STRATEGY_RANGE, and PartitionBoundInfoData::strategy.

Referenced by equalPartitionDescs().

588 {
589  int i;
590 
591  if (b1->strategy != b2->strategy)
592  return false;
593 
594  if (b1->ndatums != b2->ndatums)
595  return false;
596 
597  if (b1->null_index != b2->null_index)
598  return false;
599 
600  for (i = 0; i < b1->ndatums; i++)
601  {
602  int j;
603 
604  for (j = 0; j < partnatts; j++)
605  {
606  /* For range partitions, the bounds might not be finite. */
607  if (b1->kind != NULL)
608  {
609  /* The different kinds of bound all differ from each other */
610  if (b1->kind[i][j] != b2->kind[i][j])
611  return false;
612 
613  /* Non-finite bounds are equal without further examination. */
614  if (b1->kind[i][j] != PARTITION_RANGE_DATUM_VALUE)
615  continue;
616  }
617 
618  /*
619  * Compare the actual values. Note that it would be both incorrect
620  * and unsafe to invoke the comparison operator derived from the
621  * partitioning specification here. It would be incorrect because
622  * we want the relcache entry to be updated for ANY change to the
623  * partition bounds, not just those that the partitioning operator
624  * thinks are significant. It would be unsafe because we might
625  * reach this code in the context of an aborted transaction, and
626  * an arbitrary partitioning operator might not be safe in that
627  * context. datumIsEqual() should be simple enough to be safe.
628  */
629  if (!datumIsEqual(b1->datums[i][j], b2->datums[i][j],
630  parttypbyval[j], parttyplen[j]))
631  return false;
632  }
633 
634  if (b1->indexes[i] != b2->indexes[i])
635  return false;
636  }
637 
638  /* There are ndatums+1 indexes in case of range partitions */
639  if (b1->strategy == PARTITION_STRATEGY_RANGE &&
640  b1->indexes[i] != b2->indexes[i])
641  return false;
642 
643  return true;
644 }
PartitionRangeDatumKind ** kind
Definition: partition.c:76
bool datumIsEqual(Datum value1, Datum value2, bool typByVal, int typLen)
Definition: datum.c:219
#define NULL
Definition: c.h:229
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:787
int i
static int32 partition_rbound_cmp ( PartitionKey  key,
Datum datums1,
PartitionRangeDatumKind kind1,
bool  lower1,
PartitionRangeBound b2 
)
static

Definition at line 2158 of file partition.c.

References DatumGetInt32, PartitionRangeBound::datums, FunctionCall2Coll(), i, PartitionRangeBound::kind, PartitionRangeBound::lower, PartitionKeyData::partcollation, PARTITION_RANGE_DATUM_VALUE, PartitionKeyData::partnatts, and PartitionKeyData::partsupfunc.

Referenced by check_new_partition_bound(), partition_bound_cmp(), and qsort_partition_rbound_cmp().

2161 {
2162  int32 cmpval = 0; /* placate compiler */
2163  int i;
2164  Datum *datums2 = b2->datums;
2165  PartitionRangeDatumKind *kind2 = b2->kind;
2166  bool lower2 = b2->lower;
2167 
2168  for (i = 0; i < key->partnatts; i++)
2169  {
2170  /*
2171  * First, handle cases where the column is unbounded, which should not
2172  * invoke the comparison procedure, and should not consider any later
2173  * columns. Note that the PartitionRangeDatumKind enum elements
2174  * compare the same way as the values they represent.
2175  */
2176  if (kind1[i] < kind2[i])
2177  return -1;
2178  else if (kind1[i] > kind2[i])
2179  return 1;
2180  else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
2181 
2182  /*
2183  * The column bounds are both MINVALUE or both MAXVALUE. No later
2184  * columns should be considered, but we still need to compare
2185  * whether they are upper or lower bounds.
2186  */
2187  break;
2188 
2189  cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
2190  key->partcollation[i],
2191  datums1[i],
2192  datums2[i]));
2193  if (cmpval != 0)
2194  break;
2195  }
2196 
2197  /*
2198  * If the comparison is anything other than equal, we're done. If they
2199  * compare equal though, we still have to consider whether the boundaries
2200  * are inclusive or exclusive. Exclusive one is considered smaller of the
2201  * two.
2202  */
2203  if (cmpval == 0 && lower1 != lower2)
2204  cmpval = lower1 ? 1 : -1;
2205 
2206  return cmpval;
2207 }
PartitionRangeDatumKind * kind
Definition: partition.c:104
#define DatumGetInt32(X)
Definition: postgres.h:478
FmgrInfo * partsupfunc
Definition: rel.h:63
PartitionRangeDatumKind
Definition: parsenodes.h:816
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1047
signed int int32
Definition: c.h:256
Oid * partcollation
Definition: rel.h:66
uintptr_t Datum
Definition: postgres.h:372
int16 partnatts
Definition: rel.h:55
int i
static int32 partition_rbound_datum_cmp ( PartitionKey  key,
Datum rb_datums,
PartitionRangeDatumKind rb_kind,
Datum tuple_datums 
)
static

Definition at line 2216 of file partition.c.

References DatumGetInt32, FunctionCall2Coll(), i, PartitionKeyData::partcollation, PARTITION_RANGE_DATUM_MAXVALUE, PARTITION_RANGE_DATUM_MINVALUE, PartitionKeyData::partnatts, and PartitionKeyData::partsupfunc.

Referenced by partition_bound_cmp().

2219 {
2220  int i;
2221  int32 cmpval = -1;
2222 
2223  for (i = 0; i < key->partnatts; i++)
2224  {
2225  if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
2226  return -1;
2227  else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
2228  return 1;
2229 
2230  cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
2231  key->partcollation[i],
2232  rb_datums[i],
2233  tuple_datums[i]));
2234  if (cmpval != 0)
2235  break;
2236  }
2237 
2238  return cmpval;
2239 }
#define DatumGetInt32(X)
Definition: postgres.h:478
FmgrInfo * partsupfunc
Definition: rel.h:63
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1047
signed int int32
Definition: c.h:256
Oid * partcollation
Definition: rel.h:66
int16 partnatts
Definition: rel.h:55
int i
static int32 qsort_partition_list_value_cmp ( const void *  a,
const void *  b,
void *  arg 
)
static

Definition at line 2078 of file partition.c.

References DatumGetInt32, FunctionCall2Coll(), PartitionKeyData::partcollation, and PartitionKeyData::partsupfunc.

Referenced by RelationBuildPartitionDesc().

2079 {
2080  Datum val1 = (*(const PartitionListValue **) a)->value,
2081  val2 = (*(const PartitionListValue **) b)->value;
2082  PartitionKey key = (PartitionKey) arg;
2083 
2085  key->partcollation[0],
2086  val1, val2));
2087 }
#define DatumGetInt32(X)
Definition: postgres.h:478
FmgrInfo * partsupfunc
Definition: rel.h:63
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1047
Oid * partcollation
Definition: rel.h:66
uintptr_t Datum
Definition: postgres.h:372
struct PartitionKeyData * PartitionKey
Definition: rel.h:77
void * arg
static int32 qsort_partition_rbound_cmp ( const void *  a,
const void *  b,
void *  arg 
)
static

Definition at line 2135 of file partition.c.

References PartitionRangeBound::datums, PartitionRangeBound::kind, PartitionRangeBound::lower, and partition_rbound_cmp().

Referenced by RelationBuildPartitionDesc().

2136 {
2137  PartitionRangeBound *b1 = (*(PartitionRangeBound *const *) a);
2138  PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
2139  PartitionKey key = (PartitionKey) arg;
2140 
2141  return partition_rbound_cmp(key, b1->datums, b1->kind, b1->lower, b2);
2142 }
PartitionRangeDatumKind * kind
Definition: partition.c:104
static int32 partition_rbound_cmp(PartitionKey key, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partition.c:2158
struct PartitionKeyData * PartitionKey
Definition: rel.h:77
void * arg
void RelationBuildPartitionDesc ( Relation  rel)

Definition at line 151 of file partition.c.

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate(), Anum_pg_class_relpartbound, Assert, PartitionDescData::boundinfo, CacheMemoryContext, castNode, Const::constisnull, Const::constvalue, cur, datumCopy(), DatumGetInt32, PartitionBoundInfoData::datums, PartitionRangeBound::datums, elog, ERROR, find_inheritance_children(), FunctionCall2Coll(), GETSTRUCT, HeapTupleIsValid, i, PartitionListValue::index, PartitionRangeBound::index, PartitionBoundInfoData::indexes, PartitionBoundInfoData::kind, PartitionRangeBound::kind, lappend(), lappend_oid(), lfirst, lfirst_oid, list_length(), PartitionBoundSpec::listdatums, lower(), PartitionBoundSpec::lowerdatums, make_one_range_bound(), MemoryContextSwitchTo(), PartitionBoundInfoData::ndatums, NIL, NoLock, PartitionDescData::nparts, NULL, PartitionBoundInfoData::null_index, PartitionDescData::oids, palloc(), palloc0(), PartitionKeyData::partcollation, PARTITION_RANGE_DATUM_VALUE, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionKeyData::partnatts, PartitionKeyData::partsupfunc, PartitionKeyData::parttypbyval, PartitionKeyData::parttyplen, pfree(), qsort_arg(), qsort_partition_list_value_cmp(), qsort_partition_rbound_cmp(), RelationData::rd_partdesc, RelationData::rd_pdcxt, RelationGetPartitionKey, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RELOID, result, SearchSysCache1, PartitionKeyData::strategy, PartitionBoundInfoData::strategy, PartitionBoundSpec::strategy, stringToNode(), SysCacheGetAttr(), TextDatumGetCString, upper(), PartitionBoundSpec::upperdatums, val, PartitionListValue::value, and value.

Referenced by RelationBuildDesc(), and RelationCacheInitializePhase3().

152 {
153  List *inhoids,
154  *partoids;
155  Oid *oids = NULL;
156  List *boundspecs = NIL;
157  ListCell *cell;
158  int i,
159  nparts;
162  MemoryContext oldcxt;
163 
164  int ndatums = 0;
165 
166  /* List partitioning specific */
167  PartitionListValue **all_values = NULL;
168  int null_index = -1;
169 
170  /* Range partitioning specific */
171  PartitionRangeBound **rbounds = NULL;
172 
173  /*
174  * The following could happen in situations where rel has a pg_class entry
175  * but not the pg_partitioned_table entry yet.
176  */
177  if (key == NULL)
178  return;
179 
180  /* Get partition oids from pg_inherits */
182 
183  /* Collect bound spec nodes in a list */
184  i = 0;
185  partoids = NIL;
186  foreach(cell, inhoids)
187  {
188  Oid inhrelid = lfirst_oid(cell);
189  HeapTuple tuple;
190  Datum datum;
191  bool isnull;
192  Node *boundspec;
193 
194  tuple = SearchSysCache1(RELOID, inhrelid);
195  if (!HeapTupleIsValid(tuple))
196  elog(ERROR, "cache lookup failed for relation %u", inhrelid);
197 
198  /*
199  * It is possible that the pg_class tuple of a partition has not been
200  * updated yet to set its relpartbound field. The only case where
201  * this happens is when we open the parent relation to check using its
202  * partition descriptor that a new partition's bound does not overlap
203  * some existing partition.
204  */
205  if (!((Form_pg_class) GETSTRUCT(tuple))->relispartition)
206  {
207  ReleaseSysCache(tuple);
208  continue;
209  }
210 
211  datum = SysCacheGetAttr(RELOID, tuple,
213  &isnull);
214  Assert(!isnull);
215  boundspec = (Node *) stringToNode(TextDatumGetCString(datum));
216  boundspecs = lappend(boundspecs, boundspec);
217  partoids = lappend_oid(partoids, inhrelid);
218  ReleaseSysCache(tuple);
219  }
220 
221  nparts = list_length(partoids);
222 
223  if (nparts > 0)
224  {
225  oids = (Oid *) palloc(nparts * sizeof(Oid));
226  i = 0;
227  foreach(cell, partoids)
228  oids[i++] = lfirst_oid(cell);
229 
230  /* Convert from node to the internal representation */
231  if (key->strategy == PARTITION_STRATEGY_LIST)
232  {
233  List *non_null_values = NIL;
234 
235  /*
236  * Create a unified list of non-null values across all partitions.
237  */
238  i = 0;
239  null_index = -1;
240  foreach(cell, boundspecs)
241  {
243  lfirst(cell));
244  ListCell *c;
245 
246  if (spec->strategy != PARTITION_STRATEGY_LIST)
247  elog(ERROR, "invalid strategy in partition bound spec");
248 
249  foreach(c, spec->listdatums)
250  {
251  Const *val = castNode(Const, lfirst(c));
252  PartitionListValue *list_value = NULL;
253 
254  if (!val->constisnull)
255  {
256  list_value = (PartitionListValue *)
257  palloc0(sizeof(PartitionListValue));
258  list_value->index = i;
259  list_value->value = val->constvalue;
260  }
261  else
262  {
263  /*
264  * Never put a null into the values array, flag
265  * instead for the code further down below where we
266  * construct the actual relcache struct.
267  */
268  if (null_index != -1)
269  elog(ERROR, "found null more than once");
270  null_index = i;
271  }
272 
273  if (list_value)
274  non_null_values = lappend(non_null_values,
275  list_value);
276  }
277 
278  i++;
279  }
280 
281  ndatums = list_length(non_null_values);
282 
283  /*
284  * Collect all list values in one array. Alongside the value, we
285  * also save the index of partition the value comes from.
286  */
287  all_values = (PartitionListValue **) palloc(ndatums *
288  sizeof(PartitionListValue *));
289  i = 0;
290  foreach(cell, non_null_values)
291  {
292  PartitionListValue *src = lfirst(cell);
293 
294  all_values[i] = (PartitionListValue *)
295  palloc(sizeof(PartitionListValue));
296  all_values[i]->value = src->value;
297  all_values[i]->index = src->index;
298  i++;
299  }
300 
301  qsort_arg(all_values, ndatums, sizeof(PartitionListValue *),
302  qsort_partition_list_value_cmp, (void *) key);
303  }
304  else if (key->strategy == PARTITION_STRATEGY_RANGE)
305  {
306  int j,
307  k;
308  PartitionRangeBound **all_bounds,
309  *prev;
310  bool *distinct_indexes;
311 
312  all_bounds = (PartitionRangeBound **) palloc0(2 * nparts *
313  sizeof(PartitionRangeBound *));
314  distinct_indexes = (bool *) palloc(2 * nparts * sizeof(bool));
315 
316  /*
317  * Create a unified list of range bounds across all the
318  * partitions.
319  */
320  i = j = 0;
321  foreach(cell, boundspecs)
322  {
324  lfirst(cell));
326  *upper;
327 
328  if (spec->strategy != PARTITION_STRATEGY_RANGE)
329  elog(ERROR, "invalid strategy in partition bound spec");
330 
331  lower = make_one_range_bound(key, i, spec->lowerdatums,
332  true);
333  upper = make_one_range_bound(key, i, spec->upperdatums,
334  false);
335  all_bounds[j] = lower;
336  all_bounds[j + 1] = upper;
337  j += 2;
338  i++;
339  }
340  Assert(j == 2 * nparts);
341 
342  /* Sort all the bounds in ascending order */
343  qsort_arg(all_bounds, 2 * nparts,
344  sizeof(PartitionRangeBound *),
346  (void *) key);
347 
348  /*
349  * Count the number of distinct bounds to allocate an array of
350  * that size.
351  */
352  ndatums = 0;
353  prev = NULL;
354  for (i = 0; i < 2 * nparts; i++)
355  {
356  PartitionRangeBound *cur = all_bounds[i];
357  bool is_distinct = false;
358  int j;
359 
360  /* Is the current bound distinct from the previous one? */
361  for (j = 0; j < key->partnatts; j++)
362  {
363  Datum cmpval;
364 
365  if (prev == NULL || cur->kind[j] != prev->kind[j])
366  {
367  is_distinct = true;
368  break;
369  }
370 
371  /*
372  * If the bounds are both MINVALUE or MAXVALUE, stop now
373  * and treat them as equal, since any values after this
374  * point must be ignored.
375  */
376  if (cur->kind[j] != PARTITION_RANGE_DATUM_VALUE)
377  break;
378 
379  cmpval = FunctionCall2Coll(&key->partsupfunc[j],
380  key->partcollation[j],
381  cur->datums[j],
382  prev->datums[j]);
383  if (DatumGetInt32(cmpval) != 0)
384  {
385  is_distinct = true;
386  break;
387  }
388  }
389 
390  /*
391  * Count the current bound if it is distinct from the previous
392  * one. Also, store if the index i contains a distinct bound
393  * that we'd like put in the relcache array.
394  */
395  if (is_distinct)
396  {
397  distinct_indexes[i] = true;
398  ndatums++;
399  }
400  else
401  distinct_indexes[i] = false;
402 
403  prev = cur;
404  }
405 
406  /*
407  * Finally save them in an array from where they will be copied
408  * into the relcache.
409  */
410  rbounds = (PartitionRangeBound **) palloc(ndatums *
411  sizeof(PartitionRangeBound *));
412  k = 0;
413  for (i = 0; i < 2 * nparts; i++)
414  {
415  if (distinct_indexes[i])
416  rbounds[k++] = all_bounds[i];
417  }
418  Assert(k == ndatums);
419  }
420  else
421  elog(ERROR, "unexpected partition strategy: %d",
422  (int) key->strategy);
423  }
424 
425  /* Now build the actual relcache partition descriptor */
429  oldcxt = MemoryContextSwitchTo(rel->rd_pdcxt);
430 
431  result = (PartitionDescData *) palloc0(sizeof(PartitionDescData));
432  result->nparts = nparts;
433  if (nparts > 0)
434  {
435  PartitionBoundInfo boundinfo;
436  int *mapping;
437  int next_index = 0;
438 
439  result->oids = (Oid *) palloc0(nparts * sizeof(Oid));
440 
441  boundinfo = (PartitionBoundInfoData *)
443  boundinfo->strategy = key->strategy;
444  boundinfo->ndatums = ndatums;
445  boundinfo->null_index = -1;
446  boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *));
447 
448  /* Initialize mapping array with invalid values */
449  mapping = (int *) palloc(sizeof(int) * nparts);
450  for (i = 0; i < nparts; i++)
451  mapping[i] = -1;
452 
453  switch (key->strategy)
454  {
456  {
457  boundinfo->indexes = (int *) palloc(ndatums * sizeof(int));
458 
459  /*
460  * Copy values. Indexes of individual values are mapped
461  * to canonical values so that they match for any two list
462  * partitioned tables with same number of partitions and
463  * same lists per partition. One way to canonicalize is
464  * to assign the index in all_values[] of the smallest
465  * value of each partition, as the index of all of the
466  * partition's values.
467  */
468  for (i = 0; i < ndatums; i++)
469  {
470  boundinfo->datums[i] = (Datum *) palloc(sizeof(Datum));
471  boundinfo->datums[i][0] = datumCopy(all_values[i]->value,
472  key->parttypbyval[0],
473  key->parttyplen[0]);
474 
475  /* If the old index has no mapping, assign one */
476  if (mapping[all_values[i]->index] == -1)
477  mapping[all_values[i]->index] = next_index++;
478 
479  boundinfo->indexes[i] = mapping[all_values[i]->index];
480  }
481 
482  /*
483  * If null-accepting partition has no mapped index yet,
484  * assign one. This could happen if such partition
485  * accepts only null and hence not covered in the above
486  * loop which only handled non-null values.
487  */
488  if (null_index != -1)
489  {
490  Assert(null_index >= 0);
491  if (mapping[null_index] == -1)
492  mapping[null_index] = next_index++;
493  boundinfo->null_index = mapping[null_index];
494  }
495 
496  /* All partition must now have a valid mapping */
497  Assert(next_index == nparts);
498  break;
499  }
500 
502  {
503  boundinfo->kind = (PartitionRangeDatumKind **)
504  palloc(ndatums *
505  sizeof(PartitionRangeDatumKind *));
506  boundinfo->indexes = (int *) palloc((ndatums + 1) *
507  sizeof(int));
508 
509  for (i = 0; i < ndatums; i++)
510  {
511  int j;
512 
513  boundinfo->datums[i] = (Datum *) palloc(key->partnatts *
514  sizeof(Datum));
515  boundinfo->kind[i] = (PartitionRangeDatumKind *)
516  palloc(key->partnatts *
517  sizeof(PartitionRangeDatumKind));
518  for (j = 0; j < key->partnatts; j++)
519  {
520  if (rbounds[i]->kind[j] == PARTITION_RANGE_DATUM_VALUE)
521  boundinfo->datums[i][j] =
522  datumCopy(rbounds[i]->datums[j],
523  key->parttypbyval[j],
524  key->parttyplen[j]);
525  boundinfo->kind[i][j] = rbounds[i]->kind[j];
526  }
527 
528  /*
529  * There is no mapping for invalid indexes.
530  *
531  * Any lower bounds in the rbounds array have invalid
532  * indexes assigned, because the values between the
533  * previous bound (if there is one) and this (lower)
534  * bound are not part of the range of any existing
535  * partition.
536  */
537  if (rbounds[i]->lower)
538  boundinfo->indexes[i] = -1;
539  else
540  {
541  int orig_index = rbounds[i]->index;
542 
543  /* If the old index has no mapping, assign one */
544  if (mapping[orig_index] == -1)
545  mapping[orig_index] = next_index++;
546 
547  boundinfo->indexes[i] = mapping[orig_index];
548  }
549  }
550  boundinfo->indexes[i] = -1;
551  break;
552  }
553 
554  default:
555  elog(ERROR, "unexpected partition strategy: %d",
556  (int) key->strategy);
557  }
558 
559  result->boundinfo = boundinfo;
560 
561  /*
562  * Now assign OIDs from the original array into mapped indexes of the
563  * result array. Order of OIDs in the former is defined by the
564  * catalog scan that retrieved them, whereas that in the latter is
565  * defined by canonicalized representation of the list values or the
566  * range bounds.
567  */
568  for (i = 0; i < nparts; i++)
569  result->oids[mapping[i]] = oids[i];
570  pfree(mapping);
571  }
572 
573  MemoryContextSwitchTo(oldcxt);
574  rel->rd_partdesc = result;
575 }
Datum constvalue
Definition: primnodes.h:196
#define NIL
Definition: pg_list.h:69
struct PartitionDescData * rd_partdesc
Definition: rel.h:131
void * stringToNode(char *str)
Definition: read.c:38
PartitionRangeDatumKind ** kind
Definition: partition.c:76
PartitionRangeDatumKind * kind
Definition: partition.c:104
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
#define DatumGetInt32(X)
Definition: postgres.h:478
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:43
FmgrInfo * partsupfunc
Definition: rel.h:63
#define castNode(_type_, nodeptr)
Definition: nodes.h:578
PartitionRangeDatumKind
Definition: parsenodes.h:816
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
static int32 qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
Definition: partition.c:2135
Definition: nodes.h:509
struct cursor * cur
Definition: ecpg.c:28
return result
Definition: formatting.c:1633
Datum upper(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:74
char strategy
Definition: rel.h:54
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1047
unsigned int Oid
Definition: postgres_ext.h:31
List * lappend_oid(List *list, Oid datum)
Definition: list.c:164
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:156
PartitionBoundInfo boundinfo
Definition: partition.h:37
Definition: type.h:89
void pfree(void *pointer)
Definition: mcxt.c:950
#define ERROR
Definition: elog.h:43
static struct @121 value
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:165
static int32 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
Definition: partition.c:2078
char * c
#define NoLock
Definition: lockdefs.h:34
#define RelationGetRelationName(relation)
Definition: rel.h:436
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:128
List * lappend(List *list, void *datum)
Definition: list.c:128
void qsort_arg(void *base, size_t nel, size_t elsize, qsort_arg_comparator cmp, void *arg)
Definition: qsort_arg.c:113
Oid * partcollation
Definition: rel.h:66
#define TextDatumGetCString(d)
Definition: builtins.h:92
static PartitionRangeBound * make_one_range_bound(PartitionKey key, int index, List *datums, bool lower)
Definition: partition.c:2097
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:322
void * palloc0(Size size)
Definition: mcxt.c:878
uintptr_t Datum
Definition: postgres.h:372
int16 partnatts
Definition: rel.h:55
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1117
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1279
List * find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
Definition: pg_inherits.c:57
#define Anum_pg_class_relpartbound
Definition: pg_class.h:135
bool * parttypbyval
Definition: rel.h:72
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define NULL
Definition: c.h:229
MemoryContext rd_pdcxt
Definition: rel.h:130
#define Assert(condition)
Definition: c.h:676
#define lfirst(lc)
Definition: pg_list.h:106
int16 * parttyplen
Definition: rel.h:71
static int list_length(const List *l)
Definition: pg_list.h:89
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:786
#define RelationGetPartitionKey(relation)
Definition: rel.h:584
FormData_pg_class * Form_pg_class
Definition: pg_class.h:95
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:787
void * palloc(Size size)
Definition: mcxt.c:849
int i
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
#define RelationGetRelid(relation)
Definition: rel.h:416
long val
Definition: informix.c:689
bool constisnull
Definition: primnodes.h:197
#define lfirst_oid(lc)
Definition: pg_list.h:108
MemoryContext CacheMemoryContext
Definition: mcxt.c:46
PartitionDispatch* RelationGetPartitionDispatchInfo ( Relation  rel,
int *  num_parted,
List **  leaf_part_oids 
)

Definition at line 1011 of file partition.c.

References APPEND_REL_PARTITION_OIDS, convert_tuples_by_name(), forboth, get_rel_relkind(), gettext_noop, heap_open(), i, PartitionDispatchData::indexes, PartitionDispatchData::key, PartitionDispatchData::keystate, lappend(), lappend_oid(), lfirst, lfirst_oid, list_make1, MakeSingleTupleTableSlot(), NIL, NoLock, PartitionDescData::nparts, NULL, PartitionDescData::oids, palloc(), PartitionDispatchData::partdesc, RelationGetDescr, RelationGetPartitionDesc, RelationGetPartitionKey, PartitionDispatchData::reldesc, RELKIND_PARTITIONED_TABLE, PartitionDispatchData::tupmap, and PartitionDispatchData::tupslot.

Referenced by ExecSetupPartitionTupleRouting().

1013 {
1014  PartitionDispatchData **pd;
1015  List *all_parts = NIL,
1016  *all_parents = NIL,
1017  *parted_rels,
1018  *parted_rel_parents;
1019  ListCell *lc1,
1020  *lc2;
1021  int i,
1022  k,
1023  offset;
1024 
1025  /*
1026  * We rely on the relcache to traverse the partition tree to build both
1027  * the leaf partition OIDs list and the array of PartitionDispatch objects
1028  * for the partitioned tables in the tree. That means every partitioned
1029  * table in the tree must be locked, which is fine since we require the
1030  * caller to lock all the partitions anyway.
1031  *
1032  * For every partitioned table in the tree, starting with the root
1033  * partitioned table, add its relcache entry to parted_rels, while also
1034  * queuing its partitions (in the order in which they appear in the
1035  * partition descriptor) to be looked at later in the same loop. This is
1036  * a bit tricky but works because the foreach() macro doesn't fetch the
1037  * next list element until the bottom of the loop.
1038  */
1039  *num_parted = 1;
1040  parted_rels = list_make1(rel);
1041  /* Root partitioned table has no parent, so NULL for parent */
1042  parted_rel_parents = list_make1(NULL);
1043  APPEND_REL_PARTITION_OIDS(rel, all_parts, all_parents);
1044  forboth(lc1, all_parts, lc2, all_parents)
1045  {
1046  Oid partrelid = lfirst_oid(lc1);
1047  Relation parent = lfirst(lc2);
1048 
1049  if (get_rel_relkind(partrelid) == RELKIND_PARTITIONED_TABLE)
1050  {
1051  /*
1052  * Already locked by the caller. Note that it is the
1053  * responsibility of the caller to close the below relcache entry,
1054  * once done using the information being collected here (for
1055  * example, in ExecEndModifyTable).
1056  */
1057  Relation partrel = heap_open(partrelid, NoLock);
1058 
1059  (*num_parted)++;
1060  parted_rels = lappend(parted_rels, partrel);
1061  parted_rel_parents = lappend(parted_rel_parents, parent);
1062  APPEND_REL_PARTITION_OIDS(partrel, all_parts, all_parents);
1063  }
1064  }
1065 
1066  /*
1067  * We want to create two arrays - one for leaf partitions and another for
1068  * partitioned tables (including the root table and internal partitions).
1069  * While we only create the latter here, leaf partition array of suitable
1070  * objects (such as, ResultRelInfo) is created by the caller using the
1071  * list of OIDs we return. Indexes into these arrays get assigned in a
1072  * breadth-first manner, whereby partitions of any given level are placed
1073  * consecutively in the respective arrays.
1074  */
1075  pd = (PartitionDispatchData **) palloc(*num_parted *
1076  sizeof(PartitionDispatchData *));
1077  *leaf_part_oids = NIL;
1078  i = k = offset = 0;
1079  forboth(lc1, parted_rels, lc2, parted_rel_parents)
1080  {
1081  Relation partrel = lfirst(lc1);
1082  Relation parent = lfirst(lc2);
1083  PartitionKey partkey = RelationGetPartitionKey(partrel);
1084  TupleDesc tupdesc = RelationGetDescr(partrel);
1085  PartitionDesc partdesc = RelationGetPartitionDesc(partrel);
1086  int j,
1087  m;
1088 
1090  pd[i]->reldesc = partrel;
1091  pd[i]->key = partkey;
1092  pd[i]->keystate = NIL;
1093  pd[i]->partdesc = partdesc;
1094  if (parent != NULL)
1095  {
1096  /*
1097  * For every partitioned table other than root, we must store a
1098  * tuple table slot initialized with its tuple descriptor and a
1099  * tuple conversion map to convert a tuple from its parent's
1100  * rowtype to its own. That is to make sure that we are looking at
1101  * the correct row using the correct tuple descriptor when
1102  * computing its partition key for tuple routing.
1103  */
1104  pd[i]->tupslot = MakeSingleTupleTableSlot(tupdesc);
1106  tupdesc,
1107  gettext_noop("could not convert row type"));
1108  }
1109  else
1110  {
1111  /* Not required for the root partitioned table */
1112  pd[i]->tupslot = NULL;
1113  pd[i]->tupmap = NULL;
1114  }
1115  pd[i]->indexes = (int *) palloc(partdesc->nparts * sizeof(int));
1116 
1117  /*
1118  * Indexes corresponding to the internal partitions are multiplied by
1119  * -1 to distinguish them from those of leaf partitions. Encountering
1120  * an index >= 0 means we found a leaf partition, which is immediately
1121  * returned as the partition we are looking for. A negative index
1122  * means we found a partitioned table, whose PartitionDispatch object
1123  * is located at the above index multiplied back by -1. Using the
1124  * PartitionDispatch object, search is continued further down the
1125  * partition tree.
1126  */
1127  m = 0;
1128  for (j = 0; j < partdesc->nparts; j++)
1129  {
1130  Oid partrelid = partdesc->oids[j];
1131 
1132  if (get_rel_relkind(partrelid) != RELKIND_PARTITIONED_TABLE)
1133  {
1134  *leaf_part_oids = lappend_oid(*leaf_part_oids, partrelid);
1135  pd[i]->indexes[j] = k++;
1136  }
1137  else
1138  {
1139  /*
1140  * offset denotes the number of partitioned tables of upper
1141  * levels including those of the current level. Any partition
1142  * of this table must belong to the next level and hence will
1143  * be placed after the last partitioned table of this level.
1144  */
1145  pd[i]->indexes[j] = -(1 + offset + m);
1146  m++;
1147  }
1148  }
1149  i++;
1150 
1151  /*
1152  * This counts the number of partitioned tables at upper levels
1153  * including those of the current level.
1154  */
1155  offset += m;
1156  }
1157 
1158  return pd;
1159 }
#define NIL
Definition: pg_list.h:69
struct PartitionDispatchData * PartitionDispatch
Definition: partition.h:71
PartitionDesc partdesc
Definition: partition.h:65
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:180
#define RelationGetDescr(relation)
Definition: rel.h:428
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:1801
TupleConversionMap * tupmap
Definition: partition.h:67
#define gettext_noop(x)
Definition: c.h:139
unsigned int Oid
Definition: postgres_ext.h:31
List * lappend_oid(List *list, Oid datum)
Definition: list.c:164
#define list_make1(x1)
Definition: pg_list.h:139
#define NoLock
Definition: lockdefs.h:34
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc)
Definition: execTuples.c:199
TupleConversionMap * convert_tuples_by_name(TupleDesc indesc, TupleDesc outdesc, const char *msg)
Definition: tupconvert.c:210
List * lappend(List *list, void *datum)
Definition: list.c:128
#define RELKIND_PARTITIONED_TABLE
Definition: pg_class.h:168
TupleTableSlot * tupslot
Definition: partition.h:66
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
#define NULL
Definition: c.h:229
#define lfirst(lc)
Definition: pg_list.h:106
#define RelationGetPartitionKey(relation)
Definition: rel.h:584
void * palloc(Size size)
Definition: mcxt.c:849
#define APPEND_REL_PARTITION_OIDS(rel, partoids, parents)
Definition: partition.c:987
int i
Definition: pg_list.h:45
#define lfirst_oid(lc)
Definition: pg_list.h:108
#define RelationGetPartitionDesc(relation)
Definition: rel.h:632
PartitionKey key
Definition: partition.h:63
List* RelationGetPartitionQual ( Relation  rel)

Definition at line 945 of file partition.c.

References generate_partition_qual(), NIL, and RelationData::rd_rel.

Referenced by ATExecAttachPartition(), get_relation_constraints(), and InitResultRelInfo().

946 {
947  /* Quick exit */
948  if (!rel->rd_rel->relispartition)
949  return NIL;
950 
951  return generate_partition_qual(rel);
952 }
#define NIL
Definition: pg_list.h:69
Form_pg_class rd_rel
Definition: rel.h:114
static List * generate_partition_qual(Relation rel)
Definition: partition.c:1788