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_partitioned_table.h"
#include "catalog/pg_type.h"
#include "commands/tablecmds.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/prep.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 partition_bound_has_default(bi)   ((bi)->default_index != -1)
 

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 (Relation parent, PartitionBoundSpec *spec)
 
static Listget_qual_for_range (Relation parent, PartitionBoundSpec *spec, bool for_default)
 
static Listget_range_nulltest (PartitionKey key)
 
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)
 
static void get_partition_dispatch_recurse (Relation rel, Relation parent, List **pds, List **leaf_part_oids)
 
void RelationBuildPartitionDesc (Relation rel)
 
bool partition_bounds_equal (int partnatts, int16 *parttyplen, bool *parttypbyval, PartitionBoundInfo b1, PartitionBoundInfo b2)
 
PartitionBoundInfo partition_bounds_copy (PartitionBoundInfo src, PartitionKey key)
 
void check_new_partition_bound (char *relname, Relation parent, PartitionBoundSpec *spec)
 
void check_default_allows_bound (Relation parent, Relation default_rel, PartitionBoundSpec *new_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)
 
Oid get_default_oid_from_partdesc (PartitionDesc partdesc)
 
Oid get_default_partition_oid (Oid parentId)
 
void update_default_partition_oid (Oid parentId, Oid defaultPartId)
 
Listget_proposed_default_constraint (List *new_part_constraints)
 

Macro Definition Documentation

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

Typedef Documentation

Function Documentation

void check_default_allows_bound ( Relation  parent,
Relation  default_rel,
PartitionBoundSpec new_spec 
)

Definition at line 969 of file partition.c.

References AccessExclusiveLock, CHECK_FOR_INTERRUPTS, CreateExecutorState(), CreateTupleDescCopy(), ExprContext::ecxt_scantuple, ereport, errcode(), errmsg(), ERROR, ExecCheck(), ExecDropSingleTupleTableSlot(), ExecPrepareExpr(), ExecStoreTuple(), find_all_inheritors(), ForwardScanDirection, FreeExecutorState(), get_proposed_default_constraint(), get_qual_for_list(), get_qual_for_range(), GetLatestSnapshot(), GetPerTupleExprContext, GetPerTupleMemoryContext, heap_beginscan(), heap_close, heap_endscan(), heap_getnext(), heap_open(), INFO, InvalidBuffer, lfirst_oid, linitial, list_make1_oid, MakeSingleTupleTableSlot(), map_partition_varattnos(), MemoryContextSwitchTo(), NoLock, PartConstraintImpliedByRelConstraint(), PARTITION_STRATEGY_LIST, RelationData::rd_rel, RegisterSnapshot(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, RELKIND_FOREIGN_TABLE, RELKIND_PARTITIONED_TABLE, RELKIND_RELATION, ResetExprContext, PartitionBoundSpec::strategy, UnregisterSnapshot(), and WARNING.

Referenced by DefineRelation().

971 {
972  List *new_part_constraints;
973  List *def_part_constraints;
974  List *all_parts;
975  ListCell *lc;
976 
977  new_part_constraints = (new_spec->strategy == PARTITION_STRATEGY_LIST)
978  ? get_qual_for_list(parent, new_spec)
979  : get_qual_for_range(parent, new_spec, false);
980  def_part_constraints =
981  get_proposed_default_constraint(new_part_constraints);
982 
983  /*
984  * If the existing constraints on the default partition imply that it will
985  * not contain any row that would belong to the new partition, we can
986  * avoid scanning the default partition.
987  */
988  if (PartConstraintImpliedByRelConstraint(default_rel, def_part_constraints))
989  {
990  ereport(INFO,
991  (errmsg("updated partition constraint for default partition \"%s\" is implied by existing constraints",
992  RelationGetRelationName(default_rel))));
993  return;
994  }
995 
996  /*
997  * Scan the default partition and its subpartitions, and check for rows
998  * that do not satisfy the revised partition constraints.
999  */
1000  if (default_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1001  all_parts = find_all_inheritors(RelationGetRelid(default_rel),
1002  AccessExclusiveLock, NULL);
1003  else
1004  all_parts = list_make1_oid(RelationGetRelid(default_rel));
1005 
1006  foreach(lc, all_parts)
1007  {
1008  Oid part_relid = lfirst_oid(lc);
1009  Relation part_rel;
1010  Expr *constr;
1011  Expr *partition_constraint;
1012  EState *estate;
1013  HeapTuple tuple;
1014  ExprState *partqualstate = NULL;
1015  Snapshot snapshot;
1016  TupleDesc tupdesc;
1017  ExprContext *econtext;
1018  HeapScanDesc scan;
1019  MemoryContext oldCxt;
1020  TupleTableSlot *tupslot;
1021 
1022  /* Lock already taken above. */
1023  if (part_relid != RelationGetRelid(default_rel))
1024  {
1025  part_rel = heap_open(part_relid, NoLock);
1026 
1027  /*
1028  * If the partition constraints on default partition child imply
1029  * that it will not contain any row that would belong to the new
1030  * partition, we can avoid scanning the child table.
1031  */
1033  def_part_constraints))
1034  {
1035  ereport(INFO,
1036  (errmsg("updated partition constraint for default partition \"%s\" is implied by existing constraints",
1037  RelationGetRelationName(part_rel))));
1038 
1039  heap_close(part_rel, NoLock);
1040  continue;
1041  }
1042  }
1043  else
1044  part_rel = default_rel;
1045 
1046  /*
1047  * Only RELKIND_RELATION relations (i.e. leaf partitions) need to be
1048  * scanned.
1049  */
1050  if (part_rel->rd_rel->relkind != RELKIND_RELATION)
1051  {
1052  if (part_rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1053  ereport(WARNING,
1054  (errcode(ERRCODE_CHECK_VIOLATION),
1055  errmsg("skipped scanning foreign table \"%s\" which is a partition of default partition \"%s\"",
1056  RelationGetRelationName(part_rel),
1057  RelationGetRelationName(default_rel))));
1058 
1059  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
1060  heap_close(part_rel, NoLock);
1061 
1062  continue;
1063  }
1064 
1065  tupdesc = CreateTupleDescCopy(RelationGetDescr(part_rel));
1066  constr = linitial(def_part_constraints);
1067  partition_constraint = (Expr *)
1068  map_partition_varattnos((List *) constr,
1069  1, part_rel, parent, NULL);
1070  estate = CreateExecutorState();
1071 
1072  /* Build expression execution states for partition check quals */
1073  partqualstate = ExecPrepareExpr(partition_constraint, estate);
1074 
1075  econtext = GetPerTupleExprContext(estate);
1076  snapshot = RegisterSnapshot(GetLatestSnapshot());
1077  scan = heap_beginscan(part_rel, snapshot, 0, NULL);
1078  tupslot = MakeSingleTupleTableSlot(tupdesc);
1079 
1080  /*
1081  * Switch to per-tuple memory context and reset it for each tuple
1082  * produced, so we don't leak memory.
1083  */
1085 
1086  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1087  {
1088  ExecStoreTuple(tuple, tupslot, InvalidBuffer, false);
1089  econtext->ecxt_scantuple = tupslot;
1090 
1091  if (!ExecCheck(partqualstate, econtext))
1092  ereport(ERROR,
1093  (errcode(ERRCODE_CHECK_VIOLATION),
1094  errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
1095  RelationGetRelationName(default_rel))));
1096 
1097  ResetExprContext(econtext);
1099  }
1100 
1101  MemoryContextSwitchTo(oldCxt);
1102  heap_endscan(scan);
1103  UnregisterSnapshot(snapshot);
1105  FreeExecutorState(estate);
1106 
1107  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
1108  heap_close(part_rel, NoLock); /* keep the lock until commit */
1109  }
1110 }
TupleTableSlot * ExecStoreTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree)
Definition: execTuples.c:320
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:102
void heap_endscan(HeapScanDesc scan)
Definition: heapam.c:1565
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:863
#define RelationGetDescr(relation)
Definition: rel.h:428
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define InvalidBuffer
Definition: buf.h:25
int errcode(int sqlerrcode)
Definition: elog.c:575
#define INFO
Definition: elog.h:33
#define heap_close(r, l)
Definition: heapam.h:97
Form_pg_class rd_rel
Definition: rel.h:114
unsigned int Oid
Definition: postgres_ext.h:31
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:437
List * map_partition_varattnos(List *expr, int target_varno, Relation partrel, Relation parent, bool *found_whole_row)
Definition: partition.c:1207
void FreeExecutorState(EState *estate)
Definition: execUtils.c:183
#define GetPerTupleExprContext(estate)
Definition: executor.h:477
#define linitial(l)
Definition: pg_list.h:111
#define ERROR
Definition: elog.h:43
static List * get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
Definition: partition.c:1554
#define NoLock
Definition: lockdefs.h:34
List * get_proposed_default_constraint(List *new_part_constraints)
Definition: partition.c:2876
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:216
#define RelationGetRelationName(relation)
Definition: rel.h:436
#define RELKIND_FOREIGN_TABLE
Definition: pg_class.h:167
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc)
Definition: execTuples.c:199
#define ereport(elevel, rest)
Definition: elog.h:122
static List * get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default)
Definition: partition.c:1869
EState * CreateExecutorState(void)
Definition: execUtils.c:80
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:905
#define WARNING
Definition: elog.h:40
#define RELKIND_PARTITIONED_TABLE
Definition: pg_class.h:168
#define list_make1_oid(x1)
Definition: pg_list.h:151
HeapTuple heap_getnext(HeapScanDesc scan, ScanDirection direction)
Definition: heapam.c:1808
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:197
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:786
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:379
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:482
#define AccessExclusiveLock
Definition: lockdefs.h:45
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:167
int errmsg(const char *fmt,...)
Definition: elog.c:797
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition: execExpr.c:544
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98
#define RELKIND_RELATION
Definition: pg_class.h:160
Definition: pg_list.h:45
HeapScanDesc heap_beginscan(Relation relation, Snapshot snapshot, int nkeys, ScanKey key)
Definition: heapam.c:1397
#define RelationGetRelid(relation)
Definition: rel.h:416
#define ResetExprContext(econtext)
Definition: executor.h:471
#define lfirst_oid(lc)
Definition: pg_list.h:108
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:13537
void check_new_partition_bound ( char *  relname,
Relation  parent,
PartitionBoundSpec spec 
)

Definition at line 779 of file partition.c.

References Assert, PartitionDescData::boundinfo, castNode, Const::constisnull, Const::constvalue, PartitionRangeBound::datums, PartitionBoundInfoData::default_index, elog, equal(), ereport, errcode(), errdetail(), errmsg(), ERROR, get_range_partbound_string(), get_rel_name(), PartitionBoundInfoData::indexes, PartitionBoundSpec::is_default, PartitionRangeBound::kind, lfirst, PartitionBoundSpec::listdatums, PartitionBoundSpec::location, lower(), PartitionBoundSpec::lowerdatums, make_one_range_bound(), make_parsestate(), PartitionBoundInfoData::ndatums, PartitionDescData::nparts, PartitionBoundInfoData::null_index, PartitionDescData::oids, parser_errposition(), partition_bound_accepts_nulls, partition_bound_bsearch(), partition_bound_cmp(), partition_bound_has_default, 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().

781 {
783  PartitionDesc partdesc = RelationGetPartitionDesc(parent);
784  PartitionBoundInfo boundinfo = partdesc->boundinfo;
785  ParseState *pstate = make_parsestate(NULL);
786  int with = -1;
787  bool overlap = false;
788 
789  if (spec->is_default)
790  {
791  if (boundinfo == NULL || !partition_bound_has_default(boundinfo))
792  return;
793 
794  /* Default partition already exists, error out. */
795  ereport(ERROR,
796  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
797  errmsg("partition \"%s\" conflicts with existing default partition \"%s\"",
798  relname, get_rel_name(partdesc->oids[boundinfo->default_index])),
799  parser_errposition(pstate, spec->location)));
800  }
801 
802  switch (key->strategy)
803  {
805  {
807 
808  if (partdesc->nparts > 0)
809  {
810  ListCell *cell;
811 
812  Assert(boundinfo &&
813  boundinfo->strategy == PARTITION_STRATEGY_LIST &&
814  (boundinfo->ndatums > 0 ||
815  partition_bound_accepts_nulls(boundinfo) ||
816  partition_bound_has_default(boundinfo)));
817 
818  foreach(cell, spec->listdatums)
819  {
820  Const *val = castNode(Const, lfirst(cell));
821 
822  if (!val->constisnull)
823  {
824  int offset;
825  bool equal;
826 
827  offset = partition_bound_bsearch(key, boundinfo,
828  &val->constvalue,
829  true, &equal);
830  if (offset >= 0 && equal)
831  {
832  overlap = true;
833  with = boundinfo->indexes[offset];
834  break;
835  }
836  }
837  else if (partition_bound_accepts_nulls(boundinfo))
838  {
839  overlap = true;
840  with = boundinfo->null_index;
841  break;
842  }
843  }
844  }
845 
846  break;
847  }
848 
850  {
852  *upper;
853 
855  lower = make_one_range_bound(key, -1, spec->lowerdatums, true);
856  upper = make_one_range_bound(key, -1, spec->upperdatums, false);
857 
858  /*
859  * First check if the resulting range would be empty with
860  * specified lower and upper bounds
861  */
862  if (partition_rbound_cmp(key, lower->datums, lower->kind, true,
863  upper) >= 0)
864  {
865  ereport(ERROR,
866  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
867  errmsg("empty range bound specified for partition \"%s\"",
868  relname),
869  errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
872  parser_errposition(pstate, spec->location)));
873  }
874 
875  if (partdesc->nparts > 0)
876  {
877  PartitionBoundInfo boundinfo = partdesc->boundinfo;
878  int offset;
879  bool equal;
880 
881  Assert(boundinfo &&
882  boundinfo->strategy == PARTITION_STRATEGY_RANGE &&
883  (boundinfo->ndatums > 0 ||
884  partition_bound_has_default(boundinfo)));
885 
886  /*
887  * Test whether the new lower bound (which is treated
888  * inclusively as part of the new partition) lies inside
889  * an existing partition, or in a gap.
890  *
891  * If it's inside an existing partition, the bound at
892  * offset + 1 will be the upper bound of that partition,
893  * and its index will be >= 0.
894  *
895  * If it's in a gap, the bound at offset + 1 will be the
896  * lower bound of the next partition, and its index will
897  * be -1. This is also true if there is no next partition,
898  * since the index array is initialised with an extra -1
899  * at the end.
900  */
901  offset = partition_bound_bsearch(key, boundinfo, lower,
902  true, &equal);
903 
904  if (boundinfo->indexes[offset + 1] < 0)
905  {
906  /*
907  * Check that the new partition will fit in the gap.
908  * For it to fit, the new upper bound must be less
909  * than or equal to the lower bound of the next
910  * partition, if there is one.
911  */
912  if (offset + 1 < boundinfo->ndatums)
913  {
914  int32 cmpval;
915 
916  cmpval = partition_bound_cmp(key, boundinfo,
917  offset + 1, upper,
918  true);
919  if (cmpval < 0)
920  {
921  /*
922  * The new partition overlaps with the
923  * existing partition between offset + 1 and
924  * offset + 2.
925  */
926  overlap = true;
927  with = boundinfo->indexes[offset + 2];
928  }
929  }
930  }
931  else
932  {
933  /*
934  * The new partition overlaps with the existing
935  * partition between offset and offset + 1.
936  */
937  overlap = true;
938  with = boundinfo->indexes[offset + 1];
939  }
940  }
941 
942  break;
943  }
944 
945  default:
946  elog(ERROR, "unexpected partition strategy: %d",
947  (int) key->strategy);
948  }
949 
950  if (overlap)
951  {
952  Assert(with >= 0);
953  ereport(ERROR,
954  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
955  errmsg("partition \"%s\" would overlap partition \"%s\"",
956  relname, get_rel_name(partdesc->oids[with])),
957  parser_errposition(pstate, spec->location)));
958  }
959 }
Datum constvalue
Definition: primnodes.h:196
PartitionRangeDatumKind * kind
Definition: partition.c:110
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2972
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:43
#define castNode(_type_, nodeptr)
Definition: nodes.h:579
#define partition_bound_accepts_nulls(bi)
Definition: partition.c:90
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:246
PartitionBoundInfo boundinfo
Definition: partition.h:37
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
#define ERROR
Definition: elog.h:43
#define partition_bound_has_default(bi)
Definition: partition.c:91
static int partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo, void *probe, bool probe_is_bound, bool *is_equal)
Definition: partition.c:2766
static int32 partition_rbound_cmp(PartitionKey key, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partition.c:2615
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:10923
static PartitionRangeBound * make_one_range_bound(PartitionKey key, int index, List *datums, bool lower)
Definition: partition.c:2552
#define Assert(condition)
Definition: c.h:681
#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:2705
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 2300 of file partition.c.

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

Referenced by ExecFindPartition(), and get_partition_for_tuple().

2305 {
2306  ListCell *partexpr_item;
2307  int i;
2308 
2309  if (pd->key->partexprs != NIL && pd->keystate == NIL)
2310  {
2311  /* Check caller has set up context correctly */
2312  Assert(estate != NULL &&
2313  GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
2314 
2315  /* First time through, set up expression evaluation state */
2316  pd->keystate = ExecPrepareExprList(pd->key->partexprs, estate);
2317  }
2318 
2319  partexpr_item = list_head(pd->keystate);
2320  for (i = 0; i < pd->key->partnatts; i++)
2321  {
2322  AttrNumber keycol = pd->key->partattrs[i];
2323  Datum datum;
2324  bool isNull;
2325 
2326  if (keycol != 0)
2327  {
2328  /* Plain column; get the value directly from the heap tuple */
2329  datum = slot_getattr(slot, keycol, &isNull);
2330  }
2331  else
2332  {
2333  /* Expression; need to evaluate it */
2334  if (partexpr_item == NULL)
2335  elog(ERROR, "wrong number of partition key expressions");
2336  datum = ExecEvalExprSwitchContext((ExprState *) lfirst(partexpr_item),
2337  GetPerTupleExprContext(estate),
2338  &isNull);
2339  partexpr_item = lnext(partexpr_item);
2340  }
2341  values[i] = datum;
2342  isnull[i] = isNull;
2343  }
2344 
2345  if (partexpr_item != NULL)
2346  elog(ERROR, "wrong number of partition key expressions");
2347 }
#define NIL
Definition: pg_list.h:69
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:301
List * partexprs
Definition: rel.h:58
#define GetPerTupleExprContext(estate)
Definition: executor.h:477
#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 Assert(condition)
Definition: c.h:681
#define lfirst(lc)
Definition: pg_list.h:106
static Datum values[MAXATTR]
Definition: bootstrap.c:164
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 2213 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, SearchSysCache1(), stringToNode(), SysCacheGetAttr(), and TextDatumGetCString.

Referenced by get_partition_qual_relid(), and RelationGetPartitionQual().

2214 {
2215  HeapTuple tuple;
2216  MemoryContext oldcxt;
2217  Datum boundDatum;
2218  bool isnull;
2219  PartitionBoundSpec *bound;
2220  List *my_qual = NIL,
2221  *result = NIL;
2222  Relation parent;
2223  bool found_whole_row;
2224 
2225  /* Guard against stack overflow due to overly deep partition tree */
2227 
2228  /* Quick copy */
2229  if (rel->rd_partcheck != NIL)
2230  return copyObject(rel->rd_partcheck);
2231 
2232  /* Grab at least an AccessShareLock on the parent table */
2234  AccessShareLock);
2235 
2236  /* Get pg_class.relpartbound */
2237  tuple = SearchSysCache1(RELOID, RelationGetRelid(rel));
2238  if (!HeapTupleIsValid(tuple))
2239  elog(ERROR, "cache lookup failed for relation %u",
2240  RelationGetRelid(rel));
2241 
2242  boundDatum = SysCacheGetAttr(RELOID, tuple,
2244  &isnull);
2245  if (isnull) /* should not happen */
2246  elog(ERROR, "relation \"%s\" has relpartbound = null",
2248  bound = castNode(PartitionBoundSpec,
2249  stringToNode(TextDatumGetCString(boundDatum)));
2250  ReleaseSysCache(tuple);
2251 
2252  my_qual = get_qual_from_partbound(rel, parent, bound);
2253 
2254  /* Add the parent's quals to the list (if any) */
2255  if (parent->rd_rel->relispartition)
2256  result = list_concat(generate_partition_qual(parent), my_qual);
2257  else
2258  result = my_qual;
2259 
2260  /*
2261  * Change Vars to have partition's attnos instead of the parent's. We do
2262  * this after we concatenate the parent's quals, because we want every Var
2263  * in it to bear this relation's attnos. It's safe to assume varno = 1
2264  * here.
2265  */
2266  result = map_partition_varattnos(result, 1, rel, parent,
2267  &found_whole_row);
2268  /* There can never be a whole-row reference here */
2269  if (found_whole_row)
2270  elog(ERROR, "unexpected whole-row reference found in partition key");
2271 
2272  /* Save a copy in the relcache */
2274  rel->rd_partcheck = copyObject(result);
2275  MemoryContextSwitchTo(oldcxt);
2276 
2277  /* Keep the parent locked until commit */
2278  heap_close(parent, NoLock);
2279 
2280  return result;
2281 }
#define NIL
Definition: pg_list.h:69
void * stringToNode(char *str)
Definition: read.c:38
#define castNode(_type_, nodeptr)
Definition: nodes.h:579
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
#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:1164
List * map_partition_varattnos(List *expr, int target_varno, Relation partrel, Relation parent, bool *found_whole_row)
Definition: partition.c:1207
#define ERROR
Definition: elog.h:43
Oid get_partition_parent(Oid relid)
Definition: partition.c:1122
#define NoLock
Definition: lockdefs.h:34
void check_stack_depth(void)
Definition: postgres.c:3150
#define RelationGetRelationName(relation)
Definition: rel.h:436
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
#define TextDatumGetCString(d)
Definition: builtins.h:92
uintptr_t Datum
Definition: postgres.h:372
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1368
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:2213
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:623
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
Oid get_default_oid_from_partdesc ( PartitionDesc  partdesc)

Definition at line 2804 of file partition.c.

References PartitionDescData::boundinfo, PartitionBoundInfoData::default_index, InvalidOid, PartitionDescData::oids, and partition_bound_has_default.

Referenced by ATExecAttachPartition(), ATExecDetachPartition(), DefineRelation(), and StorePartitionBound().

2805 {
2806  if (partdesc && partdesc->boundinfo &&
2808  return partdesc->oids[partdesc->boundinfo->default_index];
2809 
2810  return InvalidOid;
2811 }
PartitionBoundInfo boundinfo
Definition: partition.h:37
#define partition_bound_has_default(bi)
Definition: partition.c:91
#define InvalidOid
Definition: postgres_ext.h:36
Oid get_default_partition_oid ( Oid  parentId)

Definition at line 2821 of file partition.c.

References GETSTRUCT, HeapTupleIsValid, InvalidOid, ObjectIdGetDatum, PARTRELID, ReleaseSysCache(), and SearchSysCache1().

Referenced by heap_drop_with_catalog(), and RelationBuildPartitionDesc().

2822 {
2823  HeapTuple tuple;
2824  Oid defaultPartId = InvalidOid;
2825 
2826  tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(parentId));
2827 
2828  if (HeapTupleIsValid(tuple))
2829  {
2830  Form_pg_partitioned_table part_table_form;
2831 
2832  part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
2833  defaultPartId = part_table_form->partdefid;
2834  }
2835 
2836  ReleaseSysCache(tuple);
2837  return defaultPartId;
2838 }
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
unsigned int Oid
Definition: postgres_ext.h:31
FormData_pg_partitioned_table * Form_pg_partitioned_table
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
#define InvalidOid
Definition: postgres_ext.h:36
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
static void get_partition_dispatch_recurse ( Relation  rel,
Relation  parent,
List **  pds,
List **  leaf_part_oids 
)
static

Definition at line 1338 of file partition.c.

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

Referenced by RelationGetPartitionDispatchInfo().

1340 {
1341  TupleDesc tupdesc = RelationGetDescr(rel);
1342  PartitionDesc partdesc = RelationGetPartitionDesc(rel);
1343  PartitionKey partkey = RelationGetPartitionKey(rel);
1344  PartitionDispatch pd;
1345  int i;
1346 
1348 
1349  /* Build a PartitionDispatch for this table and add it to *pds. */
1351  *pds = lappend(*pds, pd);
1352  pd->reldesc = rel;
1353  pd->key = partkey;
1354  pd->keystate = NIL;
1355  pd->partdesc = partdesc;
1356  if (parent != NULL)
1357  {
1358  /*
1359  * For every partitioned table other than the root, we must store a
1360  * tuple table slot initialized with its tuple descriptor and a tuple
1361  * conversion map to convert a tuple from its parent's rowtype to its
1362  * own. That is to make sure that we are looking at the correct row
1363  * using the correct tuple descriptor when computing its partition key
1364  * for tuple routing.
1365  */
1366  pd->tupslot = MakeSingleTupleTableSlot(tupdesc);
1368  tupdesc,
1369  gettext_noop("could not convert row type"));
1370  }
1371  else
1372  {
1373  /* Not required for the root partitioned table */
1374  pd->tupslot = NULL;
1375  pd->tupmap = NULL;
1376  }
1377 
1378  /*
1379  * Go look at each partition of this table. If it's a leaf partition,
1380  * simply add its OID to *leaf_part_oids. If it's a partitioned table,
1381  * recursively call get_partition_dispatch_recurse(), so that its
1382  * partitions are processed as well and a corresponding PartitionDispatch
1383  * object gets added to *pds.
1384  *
1385  * About the values in pd->indexes: for a leaf partition, it contains the
1386  * leaf partition's position in the global list *leaf_part_oids minus 1,
1387  * whereas for a partitioned table partition, it contains the partition's
1388  * position in the global list *pds multiplied by -1. The latter is
1389  * multiplied by -1 to distinguish partitioned tables from leaf partitions
1390  * when going through the values in pd->indexes. So, for example, when
1391  * using it during tuple-routing, encountering a value >= 0 means we found
1392  * a leaf partition. It is immediately returned as the index in the array
1393  * of ResultRelInfos of all the leaf partitions, using which we insert the
1394  * tuple into that leaf partition. A negative value means we found a
1395  * partitioned table. The value multiplied by -1 is returned as the index
1396  * in the array of PartitionDispatch objects of all partitioned tables in
1397  * the tree. This value is used to continue the search in the next level
1398  * of the partition tree.
1399  */
1400  pd->indexes = (int *) palloc(partdesc->nparts * sizeof(int));
1401  for (i = 0; i < partdesc->nparts; i++)
1402  {
1403  Oid partrelid = partdesc->oids[i];
1404 
1405  if (get_rel_relkind(partrelid) != RELKIND_PARTITIONED_TABLE)
1406  {
1407  *leaf_part_oids = lappend_oid(*leaf_part_oids, partrelid);
1408  pd->indexes[i] = list_length(*leaf_part_oids) - 1;
1409  }
1410  else
1411  {
1412  /*
1413  * We assume all tables in the partition tree were already locked
1414  * by the caller.
1415  */
1416  Relation partrel = heap_open(partrelid, NoLock);
1417 
1418  pd->indexes[i] = -list_length(*pds);
1419  get_partition_dispatch_recurse(partrel, rel, pds, leaf_part_oids);
1420  }
1421  }
1422 }
#define NIL
Definition: pg_list.h:69
struct PartitionDispatchData * PartitionDispatch
Definition: partition.h:71
PartitionDesc partdesc
Definition: partition.h:65
#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 NoLock
Definition: lockdefs.h:34
void check_stack_depth(void)
Definition: postgres.c:3150
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
static void get_partition_dispatch_recurse(Relation rel, Relation parent, List **pds, List **leaf_part_oids)
Definition: partition.c:1338
static int list_length(const List *l)
Definition: pg_list.h:89
#define RelationGetPartitionKey(relation)
Definition: rel.h:584
void * palloc(Size size)
Definition: mcxt.c:848
int i
#define RelationGetPartitionDesc(relation)
Definition: rel.h:632
PartitionKey key
Definition: partition.h:63
int get_partition_for_tuple ( PartitionDispatch pd,
TupleTableSlot slot,
EState estate,
PartitionDispatchData **  failed_at,
TupleTableSlot **  failed_slot 
)

Definition at line 2359 of file partition.c.

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

Referenced by ExecFindPartition().

2364 {
2365  PartitionDispatch parent;
2367  bool isnull[PARTITION_MAX_KEYS];
2368  int result;
2369  ExprContext *ecxt = GetPerTupleExprContext(estate);
2370  TupleTableSlot *ecxt_scantuple_old = ecxt->ecxt_scantuple;
2371 
2372  /* start with the root partitioned table */
2373  parent = pd[0];
2374  while (true)
2375  {
2376  PartitionKey key = parent->key;
2377  PartitionDesc partdesc = parent->partdesc;
2378  TupleTableSlot *myslot = parent->tupslot;
2379  TupleConversionMap *map = parent->tupmap;
2380  int cur_index = -1;
2381 
2382  if (myslot != NULL && map != NULL)
2383  {
2384  HeapTuple tuple = ExecFetchSlotTuple(slot);
2385 
2386  ExecClearTuple(myslot);
2387  tuple = do_convert_tuple(tuple, map);
2388  ExecStoreTuple(tuple, myslot, InvalidBuffer, true);
2389  slot = myslot;
2390  }
2391 
2392  /* Quick exit */
2393  if (partdesc->nparts == 0)
2394  {
2395  *failed_at = parent;
2396  *failed_slot = slot;
2397  result = -1;
2398  goto error_exit;
2399  }
2400 
2401  /*
2402  * Extract partition key from tuple. Expression evaluation machinery
2403  * that FormPartitionKeyDatum() invokes expects ecxt_scantuple to
2404  * point to the correct tuple slot. The slot might have changed from
2405  * what was used for the parent table if the table of the current
2406  * partitioning level has different tuple descriptor from the parent.
2407  * So update ecxt_scantuple accordingly.
2408  */
2409  ecxt->ecxt_scantuple = slot;
2410  FormPartitionKeyDatum(parent, slot, estate, values, isnull);
2411 
2412  /* Route as appropriate based on partitioning strategy. */
2413  switch (key->strategy)
2414  {
2416 
2417  if (isnull[0])
2418  {
2420  cur_index = partdesc->boundinfo->null_index;
2421  }
2422  else
2423  {
2424  bool equal = false;
2425  int cur_offset;
2426 
2427  cur_offset = partition_bound_bsearch(key,
2428  partdesc->boundinfo,
2429  values,
2430  false,
2431  &equal);
2432  if (cur_offset >= 0 && equal)
2433  cur_index = partdesc->boundinfo->indexes[cur_offset];
2434  }
2435  break;
2436 
2438  {
2439  bool equal = false,
2440  range_partkey_has_null = false;
2441  int cur_offset;
2442  int i;
2443 
2444  /*
2445  * No range includes NULL, so this will be accepted by the
2446  * default partition if there is one, and otherwise
2447  * rejected.
2448  */
2449  for (i = 0; i < key->partnatts; i++)
2450  {
2451  if (isnull[i] &&
2453  {
2454  range_partkey_has_null = true;
2455  break;
2456  }
2457  else if (isnull[i])
2458  {
2459  *failed_at = parent;
2460  *failed_slot = slot;
2461  result = -1;
2462  goto error_exit;
2463  }
2464  }
2465 
2466  /*
2467  * No need to search for partition, as the null key will
2468  * be routed to the default partition.
2469  */
2470  if (range_partkey_has_null)
2471  break;
2472 
2473  cur_offset = partition_bound_bsearch(key,
2474  partdesc->boundinfo,
2475  values,
2476  false,
2477  &equal);
2478 
2479  /*
2480  * The offset returned is such that the bound at
2481  * cur_offset is less than or equal to the tuple value, so
2482  * the bound at offset+1 is the upper bound.
2483  */
2484  cur_index = partdesc->boundinfo->indexes[cur_offset + 1];
2485  }
2486  break;
2487 
2488  default:
2489  elog(ERROR, "unexpected partition strategy: %d",
2490  (int) key->strategy);
2491  }
2492 
2493  /*
2494  * cur_index < 0 means we failed to find a partition of this parent.
2495  * Use the default partition, if there is one.
2496  */
2497  if (cur_index < 0)
2498  cur_index = partdesc->boundinfo->default_index;
2499 
2500  /*
2501  * If cur_index is still less than 0 at this point, there's no
2502  * partition for this tuple. Otherwise, we either found the leaf
2503  * partition, or a child partitioned table through which we have to
2504  * route the tuple.
2505  */
2506  if (cur_index < 0)
2507  {
2508  result = -1;
2509  *failed_at = parent;
2510  *failed_slot = slot;
2511  break;
2512  }
2513  else if (parent->indexes[cur_index] >= 0)
2514  {
2515  result = parent->indexes[cur_index];
2516  break;
2517  }
2518  else
2519  parent = pd[-parent->indexes[cur_index]];
2520  }
2521 
2522 error_exit:
2523  ecxt->ecxt_scantuple = ecxt_scantuple_old;
2524  return result;
2525 }
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:2972
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:90
#define PARTITION_MAX_KEYS
char strategy
Definition: rel.h:54
PartitionBoundInfo boundinfo
Definition: partition.h:37
#define GetPerTupleExprContext(estate)
Definition: executor.h:477
#define ERROR
Definition: elog.h:43
#define partition_bound_has_default(bi)
Definition: partition.c:91
static int partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo, void *probe, bool probe_is_bound, bool *is_equal)
Definition: partition.c:2766
void FormPartitionKeyDatum(PartitionDispatch pd, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
Definition: partition.c:2300
uintptr_t Datum
Definition: postgres.h:372
int16 partnatts
Definition: rel.h:55
TupleTableSlot * tupslot
Definition: partition.h:66
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:164
#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 1433 of file partition.c.

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

Referenced by make_partition_op_expr().

1435 {
1436  Oid operoid;
1437 
1438  /*
1439  * First check if there exists an operator of the given strategy, with
1440  * this column's type as both its lefttype and righttype, in the
1441  * partitioning operator family specified for the column.
1442  */
1443  operoid = get_opfamily_member(key->partopfamily[col],
1444  key->parttypid[col],
1445  key->parttypid[col],
1446  strategy);
1447 
1448  /*
1449  * If one doesn't exist, we must resort to using an operator in the same
1450  * operator family but with the operator class declared input type. It is
1451  * OK to do so, because the column's type is known to be binary-coercible
1452  * with the operator class input type (otherwise, the operator class in
1453  * question would not have been accepted as the partitioning operator
1454  * class). We must however inform the caller to wrap the non-Const
1455  * expression with a RelabelType node to denote the implicit coercion. It
1456  * ensures that the resulting expression structurally matches similarly
1457  * processed expressions within the optimizer.
1458  */
1459  if (!OidIsValid(operoid))
1460  {
1461  operoid = get_opfamily_member(key->partopfamily[col],
1462  key->partopcintype[col],
1463  key->partopcintype[col],
1464  strategy);
1465  if (!OidIsValid(operoid))
1466  elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
1467  strategy, key->partopcintype[col], key->partopcintype[col],
1468  key->partopfamily[col]);
1469  *need_relabel = true;
1470  }
1471  else
1472  *need_relabel = false;
1473 
1474  return operoid;
1475 }
Oid * partopfamily
Definition: rel.h:61
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:532
#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 1122 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, ObjectIdGetDatum, ScanKeyInit(), systable_beginscan(), systable_endscan(), and systable_getnext().

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

1123 {
1124  Form_pg_inherits form;
1125  Relation catalogRelation;
1126  SysScanDesc scan;
1127  ScanKeyData key[2];
1128  HeapTuple tuple;
1129  Oid result;
1130 
1131  catalogRelation = heap_open(InheritsRelationId, AccessShareLock);
1132 
1133  ScanKeyInit(&key[0],
1135  BTEqualStrategyNumber, F_OIDEQ,
1136  ObjectIdGetDatum(relid));
1137  ScanKeyInit(&key[1],
1139  BTEqualStrategyNumber, F_INT4EQ,
1140  Int32GetDatum(1));
1141 
1142  scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId, true,
1143  NULL, 2, key);
1144 
1145  tuple = systable_getnext(scan);
1146  if (!HeapTupleIsValid(tuple))
1147  elog(ERROR, "could not find tuple for parent of relation %u", relid);
1148 
1149  form = (Form_pg_inherits) GETSTRUCT(tuple);
1150  result = form->inhparent;
1151 
1152  systable_endscan(scan);
1153  heap_close(catalogRelation, AccessShareLock);
1154 
1155  return result;
1156 }
#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
#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 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 1257 of file partition.c.

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

Referenced by pg_get_partition_constraintdef().

1258 {
1259  Relation rel = heap_open(relid, AccessShareLock);
1260  Expr *result = NULL;
1261  List *and_args;
1262 
1263  /* Do the work only if this relation is a partition. */
1264  if (rel->rd_rel->relispartition)
1265  {
1266  and_args = generate_partition_qual(rel);
1267 
1268  if (and_args == NIL)
1269  result = NULL;
1270  else if (list_length(and_args) > 1)
1271  result = makeBoolExpr(AND_EXPR, and_args, -1);
1272  else
1273  result = linitial(and_args);
1274  }
1275 
1276  /* Keep the lock. */
1277  heap_close(rel, NoLock);
1278 
1279  return result;
1280 }
#define NIL
Definition: pg_list.h:69
#define AccessShareLock
Definition: lockdefs.h:36
#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
static int list_length(const List *l)
Definition: pg_list.h:89
static List * generate_partition_qual(Relation rel)
Definition: partition.c:2213
Definition: pg_list.h:45
List* get_proposed_default_constraint ( List new_part_constraints)

Definition at line 2876 of file partition.c.

References canonicalize_qual(), eval_const_expressions(), list_make1, make_ands_explicit(), makeBoolExpr(), and NOT_EXPR.

Referenced by ATExecAttachPartition(), and check_default_allows_bound().

2877 {
2878  Expr *defPartConstraint;
2879 
2880  defPartConstraint = make_ands_explicit(new_part_constraints);
2881 
2882  /*
2883  * Derive the partition constraints of default partition by negating the
2884  * given partition constraints. The partition constraint never evaluates
2885  * to NULL, so negating it like this is safe.
2886  */
2887  defPartConstraint = makeBoolExpr(NOT_EXPR,
2888  list_make1(defPartConstraint),
2889  -1);
2890  defPartConstraint =
2891  (Expr *) eval_const_expressions(NULL,
2892  (Node *) defPartConstraint);
2893  defPartConstraint = canonicalize_qual(defPartConstraint);
2894 
2895  return list_make1(defPartConstraint);
2896 }
Definition: nodes.h:510
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2429
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:366
Expr * make_ands_explicit(List *andclauses)
Definition: clauses.c:367
#define list_make1(x1)
Definition: pg_list.h:139
Expr * canonicalize_qual(Expr *qual)
Definition: prepqual.c:286
static List * get_qual_for_list ( Relation  parent,
PartitionBoundSpec spec 
)
static

Definition at line 1554 of file partition.c.

References NullTest::arg, NullTest::argisrow, ArrayExpr::array_collid, ArrayExpr::array_typeid, Assert, PartitionDescData::boundinfo, BTEqualStrategyNumber, castNode, Const::constisnull, copyObject, datumCopy(), PartitionBoundInfoData::datums, ArrayExpr::element_typeid, ArrayExpr::elements, get_array_type(), i, PartitionBoundSpec::is_default, IS_NOT_NULL, IS_NULL, lappend(), lfirst, linitial, list_make1, list_make2, PartitionBoundSpec::listdatums, ArrayExpr::location, NullTest::location, make_ands_explicit(), make_partition_op_expr(), makeBoolExpr(), makeConst(), makeNode, makeVar(), ArrayExpr::multidims, PartitionBoundInfoData::ndatums, NIL, NOT_EXPR, NullTest::nulltesttype, OR_EXPR, PartitionKeyData::partattrs, PartitionKeyData::partexprs, partition_bound_accepts_nulls, PartitionKeyData::partnatts, PartitionKeyData::parttypbyval, PartitionKeyData::parttypcoll, PartitionKeyData::parttypid, PartitionKeyData::parttyplen, PartitionKeyData::parttypmod, RelationGetPartitionDesc, RelationGetPartitionKey, type_is_array, and val.

Referenced by check_default_allows_bound(), and get_qual_from_partbound().

1555 {
1556  PartitionKey key = RelationGetPartitionKey(parent);
1557  List *result;
1558  Expr *keyCol;
1559  ArrayExpr *arr;
1560  Expr *opexpr;
1561  NullTest *nulltest;
1562  ListCell *cell;
1563  List *arrelems = NIL;
1564  bool list_has_null = false;
1565 
1566  /*
1567  * Only single-column list partitioning is supported, so we are worried
1568  * only about the partition key with index 0.
1569  */
1570  Assert(key->partnatts == 1);
1571 
1572  /* Construct Var or expression representing the partition column */
1573  if (key->partattrs[0] != 0)
1574  keyCol = (Expr *) makeVar(1,
1575  key->partattrs[0],
1576  key->parttypid[0],
1577  key->parttypmod[0],
1578  key->parttypcoll[0],
1579  0);
1580  else
1581  keyCol = (Expr *) copyObject(linitial(key->partexprs));
1582 
1583  /*
1584  * For default list partition, collect datums for all the partitions. The
1585  * default partition constraint should check that the partition key is
1586  * equal to none of those.
1587  */
1588  if (spec->is_default)
1589  {
1590  int i;
1591  int ndatums = 0;
1592  PartitionDesc pdesc = RelationGetPartitionDesc(parent);
1593  PartitionBoundInfo boundinfo = pdesc->boundinfo;
1594 
1595  if (boundinfo)
1596  {
1597  ndatums = boundinfo->ndatums;
1598 
1599  if (partition_bound_accepts_nulls(boundinfo))
1600  list_has_null = true;
1601  }
1602 
1603  /*
1604  * If default is the only partition, there need not be any partition
1605  * constraint on it.
1606  */
1607  if (ndatums == 0 && !list_has_null)
1608  return NIL;
1609 
1610  for (i = 0; i < ndatums; i++)
1611  {
1612  Const *val;
1613 
1614  /*
1615  * Construct Const from known-not-null datum. We must be careful
1616  * to copy the value, because our result has to be able to outlive
1617  * the relcache entry we're copying from.
1618  */
1619  val = makeConst(key->parttypid[0],
1620  key->parttypmod[0],
1621  key->parttypcoll[0],
1622  key->parttyplen[0],
1623  datumCopy(*boundinfo->datums[i],
1624  key->parttypbyval[0],
1625  key->parttyplen[0]),
1626  false, /* isnull */
1627  key->parttypbyval[0]);
1628 
1629  arrelems = lappend(arrelems, val);
1630  }
1631  }
1632  else
1633  {
1634  /*
1635  * Create list of Consts for the allowed values, excluding any nulls.
1636  */
1637  foreach(cell, spec->listdatums)
1638  {
1639  Const *val = castNode(Const, lfirst(cell));
1640 
1641  if (val->constisnull)
1642  list_has_null = true;
1643  else
1644  arrelems = lappend(arrelems, copyObject(val));
1645  }
1646  }
1647 
1648  if (arrelems)
1649  {
1650  /* Construct an ArrayExpr for the non-null partition values */
1651  arr = makeNode(ArrayExpr);
1652  arr->array_typeid = !type_is_array(key->parttypid[0])
1653  ? get_array_type(key->parttypid[0])
1654  : key->parttypid[0];
1655  arr->array_collid = key->parttypcoll[0];
1656  arr->element_typeid = key->parttypid[0];
1657  arr->elements = arrelems;
1658  arr->multidims = false;
1659  arr->location = -1;
1660 
1661  /* Generate the main expression, i.e., keyCol = ANY (arr) */
1663  keyCol, (Expr *) arr);
1664  }
1665  else
1666  {
1667  /* If there are no partition values, we don't need an = ANY expr */
1668  opexpr = NULL;
1669  }
1670 
1671  if (!list_has_null)
1672  {
1673  /*
1674  * Gin up a "col IS NOT NULL" test that will be AND'd with the main
1675  * expression. This might seem redundant, but the partition routing
1676  * machinery needs it.
1677  */
1678  nulltest = makeNode(NullTest);
1679  nulltest->arg = keyCol;
1680  nulltest->nulltesttype = IS_NOT_NULL;
1681  nulltest->argisrow = false;
1682  nulltest->location = -1;
1683 
1684  result = opexpr ? list_make2(nulltest, opexpr) : list_make1(nulltest);
1685  }
1686  else
1687  {
1688  /*
1689  * Gin up a "col IS NULL" test that will be OR'd with the main
1690  * expression.
1691  */
1692  nulltest = makeNode(NullTest);
1693  nulltest->arg = keyCol;
1694  nulltest->nulltesttype = IS_NULL;
1695  nulltest->argisrow = false;
1696  nulltest->location = -1;
1697 
1698  if (opexpr)
1699  {
1700  Expr *or;
1701 
1702  or = makeBoolExpr(OR_EXPR, list_make2(nulltest, opexpr), -1);
1703  result = list_make1(or);
1704  }
1705  else
1706  result = list_make1(nulltest);
1707  }
1708 
1709  /*
1710  * Note that, in general, applying NOT to a constraint expression doesn't
1711  * necessarily invert the set of rows it accepts, because NOT (NULL) is
1712  * NULL. However, the partition constraints we construct here never
1713  * evaluate to NULL, so applying NOT works as intended.
1714  */
1715  if (spec->is_default)
1716  {
1717  result = list_make1(make_ands_explicit(result));
1718  result = list_make1(makeBoolExpr(NOT_EXPR, result, -1));
1719  }
1720 
1721  return result;
1722 }
#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:1483
#define castNode(_type_, nodeptr)
Definition: nodes.h:579
Oid get_array_type(Oid typid)
Definition: lsyscache.c:2512
#define partition_bound_accepts_nulls(bi)
Definition: partition.c:90
Oid array_typeid
Definition: primnodes.h:952
List * partexprs
Definition: rel.h:58
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:296
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:366
PartitionBoundInfo boundinfo
Definition: partition.h:37
Expr * make_ands_explicit(List *andclauses)
Definition: clauses.c:367
#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
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:128
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:558
bool * parttypbyval
Definition: rel.h:72
#define Assert(condition)
Definition: c.h:681
#define lfirst(lc)
Definition: pg_list.h:106
int16 * parttyplen
Definition: rel.h:71
Oid array_collid
Definition: primnodes.h:953
int location
Definition: primnodes.h:1183
#define type_is_array(typid)
Definition: lsyscache.h:180
#define RelationGetPartitionKey(relation)
Definition: rel.h:584
Oid element_typeid
Definition: primnodes.h:954
int i
bool argisrow
Definition: primnodes.h:1182
#define copyObject(obj)
Definition: nodes.h:623
Definition: pg_list.h:45
long val
Definition: informix.c:689
bool constisnull
Definition: primnodes.h:197
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define RelationGetPartitionDesc(relation)
Definition: rel.h:632
static List * get_qual_for_range ( Relation  parent,
PartitionBoundSpec spec,
bool  for_default 
)
static

Definition at line 1869 of file partition.c.

References AND_EXPR, Anum_pg_class_relpartbound, Assert, BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, castNode, CreateExecutorState(), DatumGetBool, elog, ERROR, EState::es_query_cxt, ExecEvalExprSwitchContext(), ExecInitExpr(), fix_opfuncids(), for_both_cell, forboth, FreeExecutorState(), get_range_key_properties(), get_range_nulltest(), GetPerTupleExprContext, HeapTupleIsValid, i, PartitionBoundSpec::is_default, IsA, PartitionRangeDatum::kind, lappend(), lfirst, linitial, list_head(), list_length(), list_make1, lnext, PartitionBoundSpec::lowerdatums, make_partition_op_expr(), makeBoolConst(), makeBoolExpr(), MemoryContextSwitchTo(), NIL, NOT_EXPR, PartitionDescData::nparts, PartitionDescData::oids, OR_EXPR, PartitionKeyData::partexprs, PARTITION_RANGE_DATUM_MAXVALUE, PARTITION_RANGE_DATUM_MINVALUE, PARTITION_RANGE_DATUM_VALUE, PartitionKeyData::partnatts, RelationGetPartitionDesc, RelationGetPartitionKey, ReleaseSysCache(), RELOID, SearchSysCache1(), stringToNode(), SysCacheGetAttr(), TextDatumGetCString, and PartitionBoundSpec::upperdatums.

Referenced by check_default_allows_bound(), and get_qual_from_partbound().

1871 {
1872  List *result = NIL;
1873  ListCell *cell1,
1874  *cell2,
1875  *partexprs_item,
1876  *partexprs_item_saved;
1877  int i,
1878  j;
1879  PartitionRangeDatum *ldatum,
1880  *udatum;
1881  PartitionKey key = RelationGetPartitionKey(parent);
1882  Expr *keyCol;
1883  Const *lower_val,
1884  *upper_val;
1885  List *lower_or_arms,
1886  *upper_or_arms;
1887  int num_or_arms,
1888  current_or_arm;
1889  ListCell *lower_or_start_datum,
1890  *upper_or_start_datum;
1891  bool need_next_lower_arm,
1892  need_next_upper_arm;
1893 
1894  if (spec->is_default)
1895  {
1896  List *or_expr_args = NIL;
1897  PartitionDesc pdesc = RelationGetPartitionDesc(parent);
1898  Oid *inhoids = pdesc->oids;
1899  int nparts = pdesc->nparts,
1900  i;
1901 
1902  for (i = 0; i < nparts; i++)
1903  {
1904  Oid inhrelid = inhoids[i];
1905  HeapTuple tuple;
1906  Datum datum;
1907  bool isnull;
1908  PartitionBoundSpec *bspec;
1909 
1910  tuple = SearchSysCache1(RELOID, inhrelid);
1911  if (!HeapTupleIsValid(tuple))
1912  elog(ERROR, "cache lookup failed for relation %u", inhrelid);
1913 
1914  datum = SysCacheGetAttr(RELOID, tuple,
1916  &isnull);
1917 
1918  Assert(!isnull);
1919  bspec = (PartitionBoundSpec *)
1921  if (!IsA(bspec, PartitionBoundSpec))
1922  elog(ERROR, "expected PartitionBoundSpec");
1923 
1924  if (!bspec->is_default)
1925  {
1926  List *part_qual;
1927 
1928  part_qual = get_qual_for_range(parent, bspec, true);
1929 
1930  /*
1931  * AND the constraints of the partition and add to
1932  * or_expr_args
1933  */
1934  or_expr_args = lappend(or_expr_args, list_length(part_qual) > 1
1935  ? makeBoolExpr(AND_EXPR, part_qual, -1)
1936  : linitial(part_qual));
1937  }
1938  ReleaseSysCache(tuple);
1939  }
1940 
1941  if (or_expr_args != NIL)
1942  {
1943  /* OR all the non-default partition constraints; then negate it */
1944  result = lappend(result,
1945  list_length(or_expr_args) > 1
1946  ? makeBoolExpr(OR_EXPR, or_expr_args, -1)
1947  : linitial(or_expr_args));
1948  result = list_make1(makeBoolExpr(NOT_EXPR, result, -1));
1949  }
1950 
1951  return result;
1952  }
1953 
1954  lower_or_start_datum = list_head(spec->lowerdatums);
1955  upper_or_start_datum = list_head(spec->upperdatums);
1956  num_or_arms = key->partnatts;
1957 
1958  /*
1959  * If it is the recursive call for default, we skip the get_range_nulltest
1960  * to avoid accumulating the NullTest on the same keys for each partition.
1961  */
1962  if (!for_default)
1963  result = get_range_nulltest(key);
1964 
1965  /*
1966  * Iterate over the key columns and check if the corresponding lower and
1967  * upper datums are equal using the btree equality operator for the
1968  * column's type. If equal, we emit single keyCol = common_value
1969  * expression. Starting from the first column for which the corresponding
1970  * lower and upper bound datums are not equal, we generate OR expressions
1971  * as shown in the function's header comment.
1972  */
1973  i = 0;
1974  partexprs_item = list_head(key->partexprs);
1975  partexprs_item_saved = partexprs_item; /* placate compiler */
1976  forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
1977  {
1978  EState *estate;
1979  MemoryContext oldcxt;
1980  Expr *test_expr;
1981  ExprState *test_exprstate;
1982  Datum test_result;
1983  bool isNull;
1984 
1985  ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
1986  udatum = castNode(PartitionRangeDatum, lfirst(cell2));
1987 
1988  /*
1989  * Since get_range_key_properties() modifies partexprs_item, and we
1990  * might need to start over from the previous expression in the later
1991  * part of this function, save away the current value.
1992  */
1993  partexprs_item_saved = partexprs_item;
1994 
1995  get_range_key_properties(key, i, ldatum, udatum,
1996  &partexprs_item,
1997  &keyCol,
1998  &lower_val, &upper_val);
1999 
2000  /*
2001  * If either value is NULL, the corresponding partition bound is
2002  * either MINVALUE or MAXVALUE, and we treat them as unequal, because
2003  * even if they're the same, there is no common value to equate the
2004  * key column with.
2005  */
2006  if (!lower_val || !upper_val)
2007  break;
2008 
2009  /* Create the test expression */
2010  estate = CreateExecutorState();
2011  oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
2012  test_expr = make_partition_op_expr(key, i, BTEqualStrategyNumber,
2013  (Expr *) lower_val,
2014  (Expr *) upper_val);
2015  fix_opfuncids((Node *) test_expr);
2016  test_exprstate = ExecInitExpr(test_expr, NULL);
2017  test_result = ExecEvalExprSwitchContext(test_exprstate,
2018  GetPerTupleExprContext(estate),
2019  &isNull);
2020  MemoryContextSwitchTo(oldcxt);
2021  FreeExecutorState(estate);
2022 
2023  /* If not equal, go generate the OR expressions */
2024  if (!DatumGetBool(test_result))
2025  break;
2026 
2027  /*
2028  * The bounds for the last key column can't be equal, because such a
2029  * range partition would never be allowed to be defined (it would have
2030  * an empty range otherwise).
2031  */
2032  if (i == key->partnatts - 1)
2033  elog(ERROR, "invalid range bound specification");
2034 
2035  /* Equal, so generate keyCol = lower_val expression */
2036  result = lappend(result,
2038  keyCol, (Expr *) lower_val));
2039 
2040  i++;
2041  }
2042 
2043  /* First pair of lower_val and upper_val that are not equal. */
2044  lower_or_start_datum = cell1;
2045  upper_or_start_datum = cell2;
2046 
2047  /* OR will have as many arms as there are key columns left. */
2048  num_or_arms = key->partnatts - i;
2049  current_or_arm = 0;
2050  lower_or_arms = upper_or_arms = NIL;
2051  need_next_lower_arm = need_next_upper_arm = true;
2052  while (current_or_arm < num_or_arms)
2053  {
2054  List *lower_or_arm_args = NIL,
2055  *upper_or_arm_args = NIL;
2056 
2057  /* Restart scan of columns from the i'th one */
2058  j = i;
2059  partexprs_item = partexprs_item_saved;
2060 
2061  for_both_cell(cell1, lower_or_start_datum, cell2, upper_or_start_datum)
2062  {
2063  PartitionRangeDatum *ldatum_next = NULL,
2064  *udatum_next = NULL;
2065 
2066  ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
2067  if (lnext(cell1))
2068  ldatum_next = castNode(PartitionRangeDatum,
2069  lfirst(lnext(cell1)));
2070  udatum = castNode(PartitionRangeDatum, lfirst(cell2));
2071  if (lnext(cell2))
2072  udatum_next = castNode(PartitionRangeDatum,
2073  lfirst(lnext(cell2)));
2074  get_range_key_properties(key, j, ldatum, udatum,
2075  &partexprs_item,
2076  &keyCol,
2077  &lower_val, &upper_val);
2078 
2079  if (need_next_lower_arm && lower_val)
2080  {
2081  uint16 strategy;
2082 
2083  /*
2084  * For the non-last columns of this arm, use the EQ operator.
2085  * For the last column of this arm, use GT, unless this is the
2086  * last column of the whole bound check, or the next bound
2087  * datum is MINVALUE, in which case use GE.
2088  */
2089  if (j - i < current_or_arm)
2090  strategy = BTEqualStrategyNumber;
2091  else if (j == key->partnatts - 1 ||
2092  (ldatum_next &&
2093  ldatum_next->kind == PARTITION_RANGE_DATUM_MINVALUE))
2094  strategy = BTGreaterEqualStrategyNumber;
2095  else
2096  strategy = BTGreaterStrategyNumber;
2097 
2098  lower_or_arm_args = lappend(lower_or_arm_args,
2099  make_partition_op_expr(key, j,
2100  strategy,
2101  keyCol,
2102  (Expr *) lower_val));
2103  }
2104 
2105  if (need_next_upper_arm && upper_val)
2106  {
2107  uint16 strategy;
2108 
2109  /*
2110  * For the non-last columns of this arm, use the EQ operator.
2111  * For the last column of this arm, use LT, unless the next
2112  * bound datum is MAXVALUE, in which case use LE.
2113  */
2114  if (j - i < current_or_arm)
2115  strategy = BTEqualStrategyNumber;
2116  else if (udatum_next &&
2117  udatum_next->kind == PARTITION_RANGE_DATUM_MAXVALUE)
2118  strategy = BTLessEqualStrategyNumber;
2119  else
2120  strategy = BTLessStrategyNumber;
2121 
2122  upper_or_arm_args = lappend(upper_or_arm_args,
2123  make_partition_op_expr(key, j,
2124  strategy,
2125  keyCol,
2126  (Expr *) upper_val));
2127  }
2128 
2129  /*
2130  * Did we generate enough of OR's arguments? First arm considers
2131  * the first of the remaining columns, second arm considers first
2132  * two of the remaining columns, and so on.
2133  */
2134  ++j;
2135  if (j - i > current_or_arm)
2136  {
2137  /*
2138  * We must not emit any more arms if the new column that will
2139  * be considered is unbounded, or this one was.
2140  */
2141  if (!lower_val || !ldatum_next ||
2142  ldatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
2143  need_next_lower_arm = false;
2144  if (!upper_val || !udatum_next ||
2145  udatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
2146  need_next_upper_arm = false;
2147  break;
2148  }
2149  }
2150 
2151  if (lower_or_arm_args != NIL)
2152  lower_or_arms = lappend(lower_or_arms,
2153  list_length(lower_or_arm_args) > 1
2154  ? makeBoolExpr(AND_EXPR, lower_or_arm_args, -1)
2155  : linitial(lower_or_arm_args));
2156 
2157  if (upper_or_arm_args != NIL)
2158  upper_or_arms = lappend(upper_or_arms,
2159  list_length(upper_or_arm_args) > 1
2160  ? makeBoolExpr(AND_EXPR, upper_or_arm_args, -1)
2161  : linitial(upper_or_arm_args));
2162 
2163  /* If no work to do in the next iteration, break away. */
2164  if (!need_next_lower_arm && !need_next_upper_arm)
2165  break;
2166 
2167  ++current_or_arm;
2168  }
2169 
2170  /*
2171  * Generate the OR expressions for each of lower and upper bounds (if
2172  * required), and append to the list of implicitly ANDed list of
2173  * expressions.
2174  */
2175  if (lower_or_arms != NIL)
2176  result = lappend(result,
2177  list_length(lower_or_arms) > 1
2178  ? makeBoolExpr(OR_EXPR, lower_or_arms, -1)
2179  : linitial(lower_or_arms));
2180  if (upper_or_arms != NIL)
2181  result = lappend(result,
2182  list_length(upper_or_arms) > 1
2183  ? makeBoolExpr(OR_EXPR, upper_or_arms, -1)
2184  : linitial(upper_or_arms));
2185 
2186  /*
2187  * As noted above, for non-default, we return list with constant TRUE. If
2188  * the result is NIL during the recursive call for default, it implies
2189  * this is the only other partition which can hold every value of the key
2190  * except NULL. Hence we return the NullTest result skipped earlier.
2191  */
2192  if (result == NIL)
2193  result = for_default
2194  ? get_range_nulltest(key)
2196 
2197  return result;
2198 }
#define NIL
Definition: pg_list.h:69
void * stringToNode(char *str)
Definition: read.c:38
#define IsA(nodeptr, _type_)
Definition: nodes.h:561
static Expr * make_partition_op_expr(PartitionKey key, int keynum, uint16 strategy, Expr *arg1, Expr *arg2)
Definition: partition.c:1483
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:301
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:180
#define castNode(_type_, nodeptr)
Definition: nodes.h:579
void fix_opfuncids(Node *node)
Definition: nodeFuncs.c:1582
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
PartitionRangeDatumKind kind
Definition: parsenodes.h:828
Definition: nodes.h:510
#define false
Definition: c.h:210
List * partexprs
Definition: rel.h:58
unsigned int Oid
Definition: postgres_ext.h:31
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:477
#define true
Definition: c.h:206
unsigned short uint16
Definition: c.h:257
MemoryContext es_query_cxt
Definition: execnodes.h:471
#define linitial(l)
Definition: pg_list.h:111
#define ERROR
Definition: elog.h:43
Node * makeBoolConst(bool value, bool isnull)
Definition: makefuncs.c:354
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:1740
#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
static List * get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default)
Definition: partition.c:1869
EState * CreateExecutorState(void)
Definition: execUtils.c:80
List * lappend(List *list, void *datum)
Definition: list.c:128
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
#define TextDatumGetCString(d)
Definition: builtins.h:92
uintptr_t Datum
Definition: postgres.h:372
int16 partnatts
Definition: rel.h:55
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1368
#define for_both_cell(cell1, initcell1, cell2, initcell2)
Definition: pg_list.h:194
#define Anum_pg_class_relpartbound
Definition: pg_class.h:135
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define Assert(condition)
Definition: c.h:681
#define lfirst(lc)
Definition: pg_list.h:106
static int list_length(const List *l)
Definition: pg_list.h:89
#define RelationGetPartitionKey(relation)
Definition: rel.h:584
int i
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:113
#define elog
Definition: elog.h:219
static List * get_range_nulltest(PartitionKey key)
Definition: partition.c:1784
#define BTLessStrategyNumber
Definition: stratnum.h:29
Definition: pg_list.h:45
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32
#define RelationGetPartitionDesc(relation)
Definition: rel.h:632
List* get_qual_from_partbound ( Relation  rel,
Relation  parent,
PartitionBoundSpec spec 
)

Definition at line 1164 of file partition.c.

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

Referenced by ATExecAttachPartition(), and generate_partition_qual().

1166 {
1167  PartitionKey key = RelationGetPartitionKey(parent);
1168  List *my_qual = NIL;
1169 
1170  Assert(key != NULL);
1171 
1172  switch (key->strategy)
1173  {
1176  my_qual = get_qual_for_list(parent, spec);
1177  break;
1178 
1181  my_qual = get_qual_for_range(parent, spec, false);
1182  break;
1183 
1184  default:
1185  elog(ERROR, "unexpected partition strategy: %d",
1186  (int) key->strategy);
1187  }
1188 
1189  return my_qual;
1190 }
#define NIL
Definition: pg_list.h:69
char strategy
Definition: rel.h:54
#define ERROR
Definition: elog.h:43
static List * get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
Definition: partition.c:1554
static List * get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default)
Definition: partition.c:1869
#define Assert(condition)
Definition: c.h:681
#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 1740 of file partition.c.

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

Referenced by get_qual_for_range().

1746 {
1747  /* Get partition key expression for this column */
1748  if (key->partattrs[keynum] != 0)
1749  {
1750  *keyCol = (Expr *) makeVar(1,
1751  key->partattrs[keynum],
1752  key->parttypid[keynum],
1753  key->parttypmod[keynum],
1754  key->parttypcoll[keynum],
1755  0);
1756  }
1757  else
1758  {
1759  if (*partexprs_item == NULL)
1760  elog(ERROR, "wrong number of partition key expressions");
1761  *keyCol = copyObject(lfirst(*partexprs_item));
1762  *partexprs_item = lnext(*partexprs_item);
1763  }
1764 
1765  /* Get appropriate Const nodes for the bounds */
1766  if (ldatum->kind == PARTITION_RANGE_DATUM_VALUE)
1767  *lower_val = castNode(Const, copyObject(ldatum->value));
1768  else
1769  *lower_val = NULL;
1770 
1771  if (udatum->kind == PARTITION_RANGE_DATUM_VALUE)
1772  *upper_val = castNode(Const, copyObject(udatum->value));
1773  else
1774  *upper_val = NULL;
1775 }
#define castNode(_type_, nodeptr)
Definition: nodes.h:579
PartitionRangeDatumKind kind
Definition: parsenodes.h:828
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 lfirst(lc)
Definition: pg_list.h:106
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:623
static List * get_range_nulltest ( PartitionKey  key)
static

Definition at line 1784 of file partition.c.

References NullTest::arg, NullTest::argisrow, copyObject, elog, ERROR, i, IS_NOT_NULL, lappend(), lfirst, list_head(), lnext, NullTest::location, makeNode, makeVar(), NIL, NullTest::nulltesttype, PartitionKeyData::partattrs, PartitionKeyData::partexprs, PartitionKeyData::partnatts, PartitionKeyData::parttypcoll, PartitionKeyData::parttypid, and PartitionKeyData::parttypmod.

Referenced by get_qual_for_range().

1785 {
1786  List *result = NIL;
1787  NullTest *nulltest;
1788  ListCell *partexprs_item;
1789  int i;
1790 
1791  partexprs_item = list_head(key->partexprs);
1792  for (i = 0; i < key->partnatts; i++)
1793  {
1794  Expr *keyCol;
1795 
1796  if (key->partattrs[i] != 0)
1797  {
1798  keyCol = (Expr *) makeVar(1,
1799  key->partattrs[i],
1800  key->parttypid[i],
1801  key->parttypmod[i],
1802  key->parttypcoll[i],
1803  0);
1804  }
1805  else
1806  {
1807  if (partexprs_item == NULL)
1808  elog(ERROR, "wrong number of partition key expressions");
1809  keyCol = copyObject(lfirst(partexprs_item));
1810  partexprs_item = lnext(partexprs_item);
1811  }
1812 
1813  nulltest = makeNode(NullTest);
1814  nulltest->arg = keyCol;
1815  nulltest->nulltesttype = IS_NOT_NULL;
1816  nulltest->argisrow = false;
1817  nulltest->location = -1;
1818  result = lappend(result, nulltest);
1819  }
1820 
1821  return result;
1822 }
#define NIL
Definition: pg_list.h:69
List * partexprs
Definition: rel.h:58
Oid * parttypcoll
Definition: rel.h:74
#define ERROR
Definition: elog.h:43
Expr * arg
Definition: primnodes.h:1180
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
List * lappend(List *list, void *datum)
Definition: list.c:128
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:558
#define lfirst(lc)
Definition: pg_list.h:106
int location
Definition: primnodes.h:1183
int i
bool argisrow
Definition: primnodes.h:1182
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:623
Definition: pg_list.h:45
static PartitionRangeBound * make_one_range_bound ( PartitionKey  key,
int  index,
List datums,
bool  lower 
)
static

Definition at line 2552 of file partition.c.

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

Referenced by check_new_partition_bound(), and RelationBuildPartitionDesc().

2553 {
2554  PartitionRangeBound *bound;
2555  ListCell *lc;
2556  int i;
2557 
2558  Assert(datums != NIL);
2559 
2560  bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
2561  bound->index = index;
2562  bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
2563  bound->kind = (PartitionRangeDatumKind *) palloc0(key->partnatts *
2564  sizeof(PartitionRangeDatumKind));
2565  bound->lower = lower;
2566 
2567  i = 0;
2568  foreach(lc, datums)
2569  {
2571 
2572  /* What's contained in this range datum? */
2573  bound->kind[i] = datum->kind;
2574 
2575  if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
2576  {
2577  Const *val = castNode(Const, datum->value);
2578 
2579  if (val->constisnull)
2580  elog(ERROR, "invalid range bound datum");
2581  bound->datums[i] = val->constvalue;
2582  }
2583 
2584  i++;
2585  }
2586 
2587  return bound;
2588 }
Datum constvalue
Definition: primnodes.h:196
#define NIL
Definition: pg_list.h:69
PartitionRangeDatumKind * kind
Definition: partition.c:110
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:43
#define castNode(_type_, nodeptr)
Definition: nodes.h:579
PartitionRangeDatumKind
Definition: parsenodes.h:817
PartitionRangeDatumKind kind
Definition: parsenodes.h:828
Definition: type.h:89
#define ERROR
Definition: elog.h:43
void * palloc0(Size size)
Definition: mcxt.c:877
uintptr_t Datum
Definition: postgres.h:372
int16 partnatts
Definition: rel.h:55
#define Assert(condition)
Definition: c.h:681
#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 1483 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(), ScalarArrayOpExpr::opfuncid, ScalarArrayOpExpr::opno, PartitionKeyData::partcollation, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionKeyData::partopcintype, PartitionKeyData::parttypcoll, PartitionKeyData::strategy, and ScalarArrayOpExpr::useOr.

Referenced by get_qual_for_list(), and get_qual_for_range().

1485 {
1486  Oid operoid;
1487  bool need_relabel = false;
1488  Expr *result = NULL;
1489 
1490  /* Get the correct btree operator for this partitioning column */
1491  operoid = get_partition_operator(key, keynum, strategy, &need_relabel);
1492 
1493  /*
1494  * Chosen operator may be such that the non-Const operand needs to be
1495  * coerced, so apply the same; see the comment in
1496  * get_partition_operator().
1497  */
1498  if (!IsA(arg1, Const) &&
1499  (need_relabel ||
1500  key->partcollation[keynum] != key->parttypcoll[keynum]))
1501  arg1 = (Expr *) makeRelabelType(arg1,
1502  key->partopcintype[keynum],
1503  -1,
1504  key->partcollation[keynum],
1506 
1507  /* Generate the actual expression */
1508  switch (key->strategy)
1509  {
1511  {
1512  ScalarArrayOpExpr *saopexpr;
1513 
1514  /* Build leftop = ANY (rightop) */
1515  saopexpr = makeNode(ScalarArrayOpExpr);
1516  saopexpr->opno = operoid;
1517  saopexpr->opfuncid = get_opcode(operoid);
1518  saopexpr->useOr = true;
1519  saopexpr->inputcollid = key->partcollation[keynum];
1520  saopexpr->args = list_make2(arg1, arg2);
1521  saopexpr->location = -1;
1522 
1523  result = (Expr *) saopexpr;
1524  break;
1525  }
1526 
1528  result = make_opclause(operoid,
1529  BOOLOID,
1530  false,
1531  arg1, arg2,
1532  InvalidOid,
1533  key->partcollation[keynum]);
1534  break;
1535 
1536  default:
1537  elog(ERROR, "invalid partitioning strategy");
1538  break;
1539  }
1540 
1541  return result;
1542 }
#define list_make2(x1, x2)
Definition: pg_list.h:140
#define IsA(nodeptr, _type_)
Definition: nodes.h:561
static Oid get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel)
Definition: partition.c:1433
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:558
#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 1207 of file partition.c.

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

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

1210 {
1211  bool my_found_whole_row = false;
1212 
1213  if (expr != NIL)
1214  {
1215  AttrNumber *part_attnos;
1216 
1217  part_attnos = convert_tuples_by_name_map(RelationGetDescr(partrel),
1218  RelationGetDescr(parent),
1219  gettext_noop("could not convert row type"));
1220  expr = (List *) map_variable_attnos((Node *) expr,
1221  target_varno, 0,
1222  part_attnos,
1223  RelationGetDescr(parent)->natts,
1224  RelationGetForm(partrel)->reltype,
1225  &my_found_whole_row);
1226  }
1227 
1228  if (found_whole_row)
1229  *found_whole_row = my_found_whole_row;
1230 
1231  return expr;
1232 }
#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:510
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 2766 of file partition.c.

References PartitionBoundInfoData::ndatums, and partition_bound_cmp().

Referenced by check_new_partition_bound(), and get_partition_for_tuple().

2768 {
2769  int lo,
2770  hi,
2771  mid;
2772 
2773  lo = -1;
2774  hi = boundinfo->ndatums - 1;
2775  while (lo < hi)
2776  {
2777  int32 cmpval;
2778 
2779  mid = (lo + hi + 1) / 2;
2780  cmpval = partition_bound_cmp(key, boundinfo, mid, probe,
2781  probe_is_bound);
2782  if (cmpval <= 0)
2783  {
2784  lo = mid;
2785  *is_equal = (cmpval == 0);
2786 
2787  if (*is_equal)
2788  break;
2789  }
2790  else
2791  hi = mid - 1;
2792  }
2793 
2794  return lo;
2795 }
signed int int32
Definition: c.h:246
static int32 partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo, int offset, void *probe, bool probe_is_bound)
Definition: partition.c:2705
static int32 partition_bound_cmp ( PartitionKey  key,
PartitionBoundInfo  boundinfo,
int  offset,
void *  probe,
bool  probe_is_bound 
)
static

Definition at line 2705 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().

2707 {
2708  Datum *bound_datums = boundinfo->datums[offset];
2709  int32 cmpval = -1;
2710 
2711  switch (key->strategy)
2712  {
2714  cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
2715  key->partcollation[0],
2716  bound_datums[0],
2717  *(Datum *) probe));
2718  break;
2719 
2721  {
2722  PartitionRangeDatumKind *kind = boundinfo->kind[offset];
2723 
2724  if (probe_is_bound)
2725  {
2726  /*
2727  * We need to pass whether the existing bound is a lower
2728  * bound, so that two equal-valued lower and upper bounds
2729  * are not regarded equal.
2730  */
2731  bool lower = boundinfo->indexes[offset] < 0;
2732 
2733  cmpval = partition_rbound_cmp(key,
2734  bound_datums, kind, lower,
2735  (PartitionRangeBound *) probe);
2736  }
2737  else
2738  cmpval = partition_rbound_datum_cmp(key,
2739  bound_datums, kind,
2740  (Datum *) probe);
2741  break;
2742  }
2743 
2744  default:
2745  elog(ERROR, "unexpected partition strategy: %d",
2746  (int) key->strategy);
2747  }
2748 
2749  return cmpval;
2750 }
PartitionRangeDatumKind ** kind
Definition: partition.c:79
#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:817
char strategy
Definition: rel.h:54
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1042
signed int int32
Definition: c.h:246
#define ERROR
Definition: elog.h:43
static int32 partition_rbound_cmp(PartitionKey key, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partition.c:2615
static int32 partition_rbound_datum_cmp(PartitionKey key, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *tuple_datums)
Definition: partition.c:2673
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
PartitionBoundInfo partition_bounds_copy ( PartitionBoundInfo  src,
PartitionKey  key 
)

Definition at line 709 of file partition.c.

References Assert, datumCopy(), PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, i, PartitionBoundInfoData::indexes, PartitionBoundInfoData::kind, PartitionBoundInfoData::ndatums, PartitionBoundInfoData::null_index, palloc(), PARTITION_RANGE_DATUM_VALUE, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionKeyData::partnatts, PartitionKeyData::parttypbyval, PartitionKeyData::parttyplen, PartitionKeyData::strategy, and PartitionBoundInfoData::strategy.

Referenced by set_relation_partition_info().

711 {
712  PartitionBoundInfo dest;
713  int i;
714  int ndatums;
715  int partnatts;
716  int num_indexes;
717 
719 
720  dest->strategy = src->strategy;
721  ndatums = dest->ndatums = src->ndatums;
722  partnatts = key->partnatts;
723 
724  /* Range partitioned table has an extra index. */
725  num_indexes = key->strategy == PARTITION_STRATEGY_RANGE ? ndatums + 1 : ndatums;
726 
727  /* List partitioned tables have only a single partition key. */
728  Assert(key->strategy != PARTITION_STRATEGY_LIST || partnatts == 1);
729 
730  dest->datums = (Datum **) palloc(sizeof(Datum *) * ndatums);
731 
732  if (src->kind != NULL)
733  {
734  dest->kind = (PartitionRangeDatumKind **) palloc(ndatums *
735  sizeof(PartitionRangeDatumKind *));
736  for (i = 0; i < ndatums; i++)
737  {
738  dest->kind[i] = (PartitionRangeDatumKind *) palloc(partnatts *
739  sizeof(PartitionRangeDatumKind));
740 
741  memcpy(dest->kind[i], src->kind[i],
742  sizeof(PartitionRangeDatumKind) * key->partnatts);
743  }
744  }
745  else
746  dest->kind = NULL;
747 
748  for (i = 0; i < ndatums; i++)
749  {
750  int j;
751  dest->datums[i] = (Datum *) palloc(sizeof(Datum) * partnatts);
752 
753  for (j = 0; j < partnatts; j++)
754  {
755  if (dest->kind == NULL ||
756  dest->kind[i][j] == PARTITION_RANGE_DATUM_VALUE)
757  dest->datums[i][j] = datumCopy(src->datums[i][j],
758  key->parttypbyval[j],
759  key->parttyplen[j]);
760  }
761  }
762 
763  dest->indexes = (int *) palloc(sizeof(int) * num_indexes);
764  memcpy(dest->indexes, src->indexes, sizeof(int) * num_indexes);
765 
766  dest->null_index = src->null_index;
767  dest->default_index = src->default_index;
768 
769  return dest;
770 }
PartitionRangeDatumKind ** kind
Definition: partition.c:79
PartitionRangeDatumKind
Definition: parsenodes.h:817
char strategy
Definition: rel.h:54
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:128
uintptr_t Datum
Definition: postgres.h:372
int16 partnatts
Definition: rel.h:55
bool * parttypbyval
Definition: rel.h:72
struct PartitionBoundInfoData * PartitionBoundInfo
Definition: partition.h:28
#define Assert(condition)
Definition: c.h:681
int16 * parttyplen
Definition: rel.h:71
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:786
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:787
void * palloc(Size size)
Definition: mcxt.c:848
int i
bool partition_bounds_equal ( int  partnatts,
int16 parttyplen,
bool parttypbyval,
PartitionBoundInfo  b1,
PartitionBoundInfo  b2 
)

Definition at line 641 of file partition.c.

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

Referenced by build_joinrel_partition_info(), equalPartitionDescs(), and try_partition_wise_join().

643 {
644  int i;
645 
646  if (b1->strategy != b2->strategy)
647  return false;
648 
649  if (b1->ndatums != b2->ndatums)
650  return false;
651 
652  if (b1->null_index != b2->null_index)
653  return false;
654 
655  if (b1->default_index != b2->default_index)
656  return false;
657 
658  for (i = 0; i < b1->ndatums; i++)
659  {
660  int j;
661 
662  for (j = 0; j < partnatts; j++)
663  {
664  /* For range partitions, the bounds might not be finite. */
665  if (b1->kind != NULL)
666  {
667  /* The different kinds of bound all differ from each other */
668  if (b1->kind[i][j] != b2->kind[i][j])
669  return false;
670 
671  /* Non-finite bounds are equal without further examination. */
672  if (b1->kind[i][j] != PARTITION_RANGE_DATUM_VALUE)
673  continue;
674  }
675 
676  /*
677  * Compare the actual values. Note that it would be both incorrect
678  * and unsafe to invoke the comparison operator derived from the
679  * partitioning specification here. It would be incorrect because
680  * we want the relcache entry to be updated for ANY change to the
681  * partition bounds, not just those that the partitioning operator
682  * thinks are significant. It would be unsafe because we might
683  * reach this code in the context of an aborted transaction, and
684  * an arbitrary partitioning operator might not be safe in that
685  * context. datumIsEqual() should be simple enough to be safe.
686  */
687  if (!datumIsEqual(b1->datums[i][j], b2->datums[i][j],
688  parttypbyval[j], parttyplen[j]))
689  return false;
690  }
691 
692  if (b1->indexes[i] != b2->indexes[i])
693  return false;
694  }
695 
696  /* There are ndatums+1 indexes in case of range partitions */
697  if (b1->strategy == PARTITION_STRATEGY_RANGE &&
698  b1->indexes[i] != b2->indexes[i])
699  return false;
700 
701  return true;
702 }
PartitionRangeDatumKind ** kind
Definition: partition.c:79
bool datumIsEqual(Datum value1, Datum value2, bool typByVal, int typLen)
Definition: datum.c:219
#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 2615 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().

2618 {
2619  int32 cmpval = 0; /* placate compiler */
2620  int i;
2621  Datum *datums2 = b2->datums;
2622  PartitionRangeDatumKind *kind2 = b2->kind;
2623  bool lower2 = b2->lower;
2624 
2625  for (i = 0; i < key->partnatts; i++)
2626  {
2627  /*
2628  * First, handle cases where the column is unbounded, which should not
2629  * invoke the comparison procedure, and should not consider any later
2630  * columns. Note that the PartitionRangeDatumKind enum elements
2631  * compare the same way as the values they represent.
2632  */
2633  if (kind1[i] < kind2[i])
2634  return -1;
2635  else if (kind1[i] > kind2[i])
2636  return 1;
2637  else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
2638 
2639  /*
2640  * The column bounds are both MINVALUE or both MAXVALUE. No later
2641  * columns should be considered, but we still need to compare
2642  * whether they are upper or lower bounds.
2643  */
2644  break;
2645 
2646  cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
2647  key->partcollation[i],
2648  datums1[i],
2649  datums2[i]));
2650  if (cmpval != 0)
2651  break;
2652  }
2653 
2654  /*
2655  * If the comparison is anything other than equal, we're done. If they
2656  * compare equal though, we still have to consider whether the boundaries
2657  * are inclusive or exclusive. Exclusive one is considered smaller of the
2658  * two.
2659  */
2660  if (cmpval == 0 && lower1 != lower2)
2661  cmpval = lower1 ? 1 : -1;
2662 
2663  return cmpval;
2664 }
PartitionRangeDatumKind * kind
Definition: partition.c:110
#define DatumGetInt32(X)
Definition: postgres.h:478
FmgrInfo * partsupfunc
Definition: rel.h:63
PartitionRangeDatumKind
Definition: parsenodes.h:817
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1042
signed int int32
Definition: c.h:246
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 2673 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().

2676 {
2677  int i;
2678  int32 cmpval = -1;
2679 
2680  for (i = 0; i < key->partnatts; i++)
2681  {
2682  if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
2683  return -1;
2684  else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
2685  return 1;
2686 
2687  cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
2688  key->partcollation[i],
2689  rb_datums[i],
2690  tuple_datums[i]));
2691  if (cmpval != 0)
2692  break;
2693  }
2694 
2695  return cmpval;
2696 }
#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:1042
signed int int32
Definition: c.h:246
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 2533 of file partition.c.

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

Referenced by RelationBuildPartitionDesc().

2534 {
2535  Datum val1 = (*(const PartitionListValue **) a)->value,
2536  val2 = (*(const PartitionListValue **) b)->value;
2537  PartitionKey key = (PartitionKey) arg;
2538 
2540  key->partcollation[0],
2541  val1, val2));
2542 }
#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:1042
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 2592 of file partition.c.

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

Referenced by RelationBuildPartitionDesc().

2593 {
2594  PartitionRangeBound *b1 = (*(PartitionRangeBound *const *) a);
2595  PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
2596  PartitionKey key = (PartitionKey) arg;
2597 
2598  return partition_rbound_cmp(key, b1->datums, b1->kind, b1->lower, b2);
2599 }
PartitionRangeDatumKind * kind
Definition: partition.c:110
static int32 partition_rbound_cmp(PartitionKey key, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partition.c:2615
struct PartitionKeyData * PartitionKey
Definition: rel.h:77
void * arg
void RelationBuildPartitionDesc ( Relation  rel)

Definition at line 161 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, PartitionBoundInfoData::default_index, elog, ERROR, find_inheritance_children(), FunctionCall2Coll(), get_default_partition_oid(), GETSTRUCT, HeapTupleIsValid, i, PartitionListValue::index, PartitionRangeBound::index, PartitionBoundInfoData::indexes, PartitionBoundSpec::is_default, 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, 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, SearchSysCache1(), PartitionKeyData::strategy, PartitionBoundInfoData::strategy, PartitionBoundSpec::strategy, stringToNode(), SysCacheGetAttr(), TextDatumGetCString, upper(), PartitionBoundSpec::upperdatums, val, PartitionListValue::value, and value.

Referenced by RelationBuildDesc(), and RelationCacheInitializePhase3().

162 {
163  List *inhoids,
164  *partoids;
165  Oid *oids = NULL;
166  List *boundspecs = NIL;
167  ListCell *cell;
168  int i,
169  nparts;
171  PartitionDesc result;
172  MemoryContext oldcxt;
173 
174  int ndatums = 0;
175  int default_index = -1;
176 
177  /* List partitioning specific */
178  PartitionListValue **all_values = NULL;
179  int null_index = -1;
180 
181  /* Range partitioning specific */
182  PartitionRangeBound **rbounds = NULL;
183 
184  /*
185  * The following could happen in situations where rel has a pg_class entry
186  * but not the pg_partitioned_table entry yet.
187  */
188  if (key == NULL)
189  return;
190 
191  /* Get partition oids from pg_inherits */
193 
194  /* Collect bound spec nodes in a list */
195  i = 0;
196  partoids = NIL;
197  foreach(cell, inhoids)
198  {
199  Oid inhrelid = lfirst_oid(cell);
200  HeapTuple tuple;
201  Datum datum;
202  bool isnull;
203  Node *boundspec;
204 
205  tuple = SearchSysCache1(RELOID, inhrelid);
206  if (!HeapTupleIsValid(tuple))
207  elog(ERROR, "cache lookup failed for relation %u", inhrelid);
208 
209  /*
210  * It is possible that the pg_class tuple of a partition has not been
211  * updated yet to set its relpartbound field. The only case where
212  * this happens is when we open the parent relation to check using its
213  * partition descriptor that a new partition's bound does not overlap
214  * some existing partition.
215  */
216  if (!((Form_pg_class) GETSTRUCT(tuple))->relispartition)
217  {
218  ReleaseSysCache(tuple);
219  continue;
220  }
221 
222  datum = SysCacheGetAttr(RELOID, tuple,
224  &isnull);
225  Assert(!isnull);
226  boundspec = (Node *) stringToNode(TextDatumGetCString(datum));
227 
228  /*
229  * Sanity check: If the PartitionBoundSpec says this is the default
230  * partition, its OID should correspond to whatever's stored in
231  * pg_partitioned_table.partdefid; if not, the catalog is corrupt.
232  */
233  if (castNode(PartitionBoundSpec, boundspec)->is_default)
234  {
235  Oid partdefid;
236 
238  if (partdefid != inhrelid)
239  elog(ERROR, "expected partdefid %u, but got %u",
240  inhrelid, partdefid);
241  }
242 
243  boundspecs = lappend(boundspecs, boundspec);
244  partoids = lappend_oid(partoids, inhrelid);
245  ReleaseSysCache(tuple);
246  }
247 
248  nparts = list_length(partoids);
249 
250  if (nparts > 0)
251  {
252  oids = (Oid *) palloc(nparts * sizeof(Oid));
253  i = 0;
254  foreach(cell, partoids)
255  oids[i++] = lfirst_oid(cell);
256 
257  /* Convert from node to the internal representation */
258  if (key->strategy == PARTITION_STRATEGY_LIST)
259  {
260  List *non_null_values = NIL;
261 
262  /*
263  * Create a unified list of non-null values across all partitions.
264  */
265  i = 0;
266  null_index = -1;
267  foreach(cell, boundspecs)
268  {
270  lfirst(cell));
271  ListCell *c;
272 
273  if (spec->strategy != PARTITION_STRATEGY_LIST)
274  elog(ERROR, "invalid strategy in partition bound spec");
275 
276  /*
277  * Note the index of the partition bound spec for the default
278  * partition. There's no datum to add to the list of non-null
279  * datums for this partition.
280  */
281  if (spec->is_default)
282  {
283  default_index = i;
284  i++;
285  continue;
286  }
287 
288  foreach(c, spec->listdatums)
289  {
290  Const *val = castNode(Const, lfirst(c));
291  PartitionListValue *list_value = NULL;
292 
293  if (!val->constisnull)
294  {
295  list_value = (PartitionListValue *)
296  palloc0(sizeof(PartitionListValue));
297  list_value->index = i;
298  list_value->value = val->constvalue;
299  }
300  else
301  {
302  /*
303  * Never put a null into the values array, flag
304  * instead for the code further down below where we
305  * construct the actual relcache struct.
306  */
307  if (null_index != -1)
308  elog(ERROR, "found null more than once");
309  null_index = i;
310  }
311 
312  if (list_value)
313  non_null_values = lappend(non_null_values,
314  list_value);
315  }
316 
317  i++;
318  }
319 
320  ndatums = list_length(non_null_values);
321 
322  /*
323  * Collect all list values in one array. Alongside the value, we
324  * also save the index of partition the value comes from.
325  */
326  all_values = (PartitionListValue **) palloc(ndatums *
327  sizeof(PartitionListValue *));
328  i = 0;
329  foreach(cell, non_null_values)
330  {
331  PartitionListValue *src = lfirst(cell);
332 
333  all_values[i] = (PartitionListValue *)
334  palloc(sizeof(PartitionListValue));
335  all_values[i]->value = src->value;
336  all_values[i]->index = src->index;
337  i++;
338  }
339 
340  qsort_arg(all_values, ndatums, sizeof(PartitionListValue *),
341  qsort_partition_list_value_cmp, (void *) key);
342  }
343  else if (key->strategy == PARTITION_STRATEGY_RANGE)
344  {
345  int k;
346  PartitionRangeBound **all_bounds,
347  *prev;
348 
349  all_bounds = (PartitionRangeBound **) palloc0(2 * nparts *
350  sizeof(PartitionRangeBound *));
351 
352  /*
353  * Create a unified list of range bounds across all the
354  * partitions.
355  */
356  i = ndatums = 0;
357  foreach(cell, boundspecs)
358  {
360  lfirst(cell));
362  *upper;
363 
364  if (spec->strategy != PARTITION_STRATEGY_RANGE)
365  elog(ERROR, "invalid strategy in partition bound spec");
366 
367  /*
368  * Note the index of the partition bound spec for the default
369  * partition. There's no datum to add to the allbounds array
370  * for this partition.
371  */
372  if (spec->is_default)
373  {
374  default_index = i++;
375  continue;
376  }
377 
378  lower = make_one_range_bound(key, i, spec->lowerdatums,
379  true);
380  upper = make_one_range_bound(key, i, spec->upperdatums,
381  false);
382  all_bounds[ndatums++] = lower;
383  all_bounds[ndatums++] = upper;
384  i++;
385  }
386 
387  Assert(ndatums == nparts * 2 ||
388  (default_index != -1 && ndatums == (nparts - 1) * 2));
389 
390  /* Sort all the bounds in ascending order */
391  qsort_arg(all_bounds, ndatums,
392  sizeof(PartitionRangeBound *),
394  (void *) key);
395 
396  /* Save distinct bounds from all_bounds into rbounds. */
397  rbounds = (PartitionRangeBound **)
398  palloc(ndatums * sizeof(PartitionRangeBound *));
399  k = 0;
400  prev = NULL;
401  for (i = 0; i < ndatums; i++)
402  {
403  PartitionRangeBound *cur = all_bounds[i];
404  bool is_distinct = false;
405  int j;
406 
407  /* Is the current bound distinct from the previous one? */
408  for (j = 0; j < key->partnatts; j++)
409  {
410  Datum cmpval;
411 
412  if (prev == NULL || cur->kind[j] != prev->kind[j])
413  {
414  is_distinct = true;
415  break;
416  }
417 
418  /*
419  * If the bounds are both MINVALUE or MAXVALUE, stop now
420  * and treat them as equal, since any values after this
421  * point must be ignored.
422  */
423  if (cur->kind[j] != PARTITION_RANGE_DATUM_VALUE)
424  break;
425 
426  cmpval = FunctionCall2Coll(&key->partsupfunc[j],
427  key->partcollation[j],
428  cur->datums[j],
429  prev->datums[j]);
430  if (DatumGetInt32(cmpval) != 0)
431  {
432  is_distinct = true;
433  break;
434  }
435  }
436 
437  /*
438  * Only if the bound is distinct save it into a temporary
439  * array i.e. rbounds which is later copied into boundinfo
440  * datums array.
441  */
442  if (is_distinct)
443  rbounds[k++] = all_bounds[i];
444 
445  prev = cur;
446  }
447 
448  /* Update ndatums to hold the count of distinct datums. */
449  ndatums = k;
450  }
451  else
452  elog(ERROR, "unexpected partition strategy: %d",
453  (int) key->strategy);
454  }
455 
456  /* Now build the actual relcache partition descriptor */
460  oldcxt = MemoryContextSwitchTo(rel->rd_pdcxt);
461 
462  result = (PartitionDescData *) palloc0(sizeof(PartitionDescData));
463  result->nparts = nparts;
464  if (nparts > 0)
465  {
466  PartitionBoundInfo boundinfo;
467  int *mapping;
468  int next_index = 0;
469 
470  result->oids = (Oid *) palloc0(nparts * sizeof(Oid));
471 
472  boundinfo = (PartitionBoundInfoData *)
474  boundinfo->strategy = key->strategy;
475  boundinfo->default_index = -1;
476  boundinfo->ndatums = ndatums;
477  boundinfo->null_index = -1;
478  boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *));
479 
480  /* Initialize mapping array with invalid values */
481  mapping = (int *) palloc(sizeof(int) * nparts);
482  for (i = 0; i < nparts; i++)
483  mapping[i] = -1;
484 
485  switch (key->strategy)
486  {
488  {
489  boundinfo->indexes = (int *) palloc(ndatums * sizeof(int));
490 
491  /*
492  * Copy values. Indexes of individual values are mapped
493  * to canonical values so that they match for any two list
494  * partitioned tables with same number of partitions and
495  * same lists per partition. One way to canonicalize is
496  * to assign the index in all_values[] of the smallest
497  * value of each partition, as the index of all of the
498  * partition's values.
499  */
500  for (i = 0; i < ndatums; i++)
501  {
502  boundinfo->datums[i] = (Datum *) palloc(sizeof(Datum));
503  boundinfo->datums[i][0] = datumCopy(all_values[i]->value,
504  key->parttypbyval[0],
505  key->parttyplen[0]);
506 
507  /* If the old index has no mapping, assign one */
508  if (mapping[all_values[i]->index] == -1)
509  mapping[all_values[i]->index] = next_index++;
510 
511  boundinfo->indexes[i] = mapping[all_values[i]->index];
512  }
513 
514  /*
515  * If null-accepting partition has no mapped index yet,
516  * assign one. This could happen if such partition
517  * accepts only null and hence not covered in the above
518  * loop which only handled non-null values.
519  */
520  if (null_index != -1)
521  {
522  Assert(null_index >= 0);
523  if (mapping[null_index] == -1)
524  mapping[null_index] = next_index++;
525  boundinfo->null_index = mapping[null_index];
526  }
527 
528  /* Assign mapped index for the default partition. */
529  if (default_index != -1)
530  {
531  /*
532  * The default partition accepts any value not
533  * specified in the lists of other partitions, hence
534  * it should not get mapped index while assigning
535  * those for non-null datums.
536  */
537  Assert(default_index >= 0 &&
538  mapping[default_index] == -1);
539  mapping[default_index] = next_index++;
540  boundinfo->default_index = mapping[default_index];
541  }
542 
543  /* All partition must now have a valid mapping */
544  Assert(next_index == nparts);
545  break;
546  }
547 
549  {
550  boundinfo->kind = (PartitionRangeDatumKind **)
551  palloc(ndatums *
552  sizeof(PartitionRangeDatumKind *));
553  boundinfo->indexes = (int *) palloc((ndatums + 1) *
554  sizeof(int));
555 
556  for (i = 0; i < ndatums; i++)
557  {
558  int j;
559 
560  boundinfo->datums[i] = (Datum *) palloc(key->partnatts *
561  sizeof(Datum));
562  boundinfo->kind[i] = (PartitionRangeDatumKind *)
563  palloc(key->partnatts *
564  sizeof(PartitionRangeDatumKind));
565  for (j = 0; j < key->partnatts; j++)
566  {
567  if (rbounds[i]->kind[j] == PARTITION_RANGE_DATUM_VALUE)
568  boundinfo->datums[i][j] =
569  datumCopy(rbounds[i]->datums[j],
570  key->parttypbyval[j],
571  key->parttyplen[j]);
572  boundinfo->kind[i][j] = rbounds[i]->kind[j];
573  }
574 
575  /*
576  * There is no mapping for invalid indexes.
577  *
578  * Any lower bounds in the rbounds array have invalid
579  * indexes assigned, because the values between the
580  * previous bound (if there is one) and this (lower)
581  * bound are not part of the range of any existing
582  * partition.
583  */
584  if (rbounds[i]->lower)
585  boundinfo->indexes[i] = -1;
586  else
587  {
588  int orig_index = rbounds[i]->index;
589 
590  /* If the old index has no mapping, assign one */
591  if (mapping[orig_index] == -1)
592  mapping[orig_index] = next_index++;
593 
594  boundinfo->indexes[i] = mapping[orig_index];
595  }
596  }
597 
598  /* Assign mapped index for the default partition. */
599  if (default_index != -1)
600  {
601  Assert(default_index >= 0 && mapping[default_index] == -1);
602  mapping[default_index] = next_index++;
603  boundinfo->default_index = mapping[default_index];
604  }
605  boundinfo->indexes[i] = -1;
606  break;
607  }
608 
609  default:
610  elog(ERROR, "unexpected partition strategy: %d",
611  (int) key->strategy);
612  }
613 
614  result->boundinfo = boundinfo;
615 
616  /*
617  * Now assign OIDs from the original array into mapped indexes of the
618  * result array. Order of OIDs in the former is defined by the
619  * catalog scan that retrieved them, whereas that in the latter is
620  * defined by canonicalized representation of the list values or the
621  * range bounds.
622  */
623  for (i = 0; i < nparts; i++)
624  result->oids[mapping[i]] = oids[i];
625  pfree(mapping);
626  }
627 
628  MemoryContextSwitchTo(oldcxt);
629  rel->rd_partdesc = result;
630 }
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:79
PartitionRangeDatumKind * kind
Definition: partition.c:110
#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:579
PartitionRangeDatumKind
Definition: parsenodes.h:817
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:2592
Definition: nodes.h:510
struct cursor * cur
Definition: ecpg.c:28
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:1042
unsigned int Oid
Definition: postgres_ext.h:31
List * lappend_oid(List *list, Oid datum)
Definition: list.c:164
PartitionBoundInfo boundinfo
Definition: partition.h:37
Definition: type.h:89
void pfree(void *pointer)
Definition: mcxt.c:949
#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:2533
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
Oid get_default_partition_oid(Oid parentId)
Definition: partition.c:2821
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
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
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:2552
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:322
void * palloc0(Size size)
Definition: mcxt.c:877
uintptr_t Datum
Definition: postgres.h:372
int16 partnatts
Definition: rel.h:55
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1368
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
MemoryContext rd_pdcxt
Definition: rel.h:130
#define Assert(condition)
Definition: c.h:681
#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:848
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 1295 of file partition.c.

References Assert, get_partition_dispatch_recurse(), i, lfirst, list_length(), NIL, palloc(), RelationData::rd_rel, and RELKIND_PARTITIONED_TABLE.

Referenced by ExecSetupPartitionTupleRouting().

1297 {
1298  List *pdlist = NIL;
1299  PartitionDispatchData **pd;
1300  ListCell *lc;
1301  int i;
1302 
1303  Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
1304 
1305  *num_parted = 0;
1306  *leaf_part_oids = NIL;
1307 
1308  get_partition_dispatch_recurse(rel, NULL, &pdlist, leaf_part_oids);
1309  *num_parted = list_length(pdlist);
1310  pd = (PartitionDispatchData **) palloc(*num_parted *
1311  sizeof(PartitionDispatchData *));
1312  i = 0;
1313  foreach(lc, pdlist)
1314  {
1315  pd[i++] = lfirst(lc);
1316  }
1317 
1318  return pd;
1319 }
#define NIL
Definition: pg_list.h:69
Form_pg_class rd_rel
Definition: rel.h:114
#define RELKIND_PARTITIONED_TABLE
Definition: pg_class.h:168
static void get_partition_dispatch_recurse(Relation rel, Relation parent, List **pds, List **leaf_part_oids)
Definition: partition.c:1338
#define Assert(condition)
Definition: c.h:681
#define lfirst(lc)
Definition: pg_list.h:106
static int list_length(const List *l)
Definition: pg_list.h:89
void * palloc(Size size)
Definition: mcxt.c:848
int i
Definition: pg_list.h:45
List* RelationGetPartitionQual ( Relation  rel)

Definition at line 1240 of file partition.c.

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

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

1241 {
1242  /* Quick exit */
1243  if (!rel->rd_rel->relispartition)
1244  return NIL;
1245 
1246  return generate_partition_qual(rel);
1247 }
#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:2213
void update_default_partition_oid ( Oid  parentId,
Oid  defaultPartId 
)

Definition at line 2846 of file partition.c.

References CatalogTupleUpdate(), elog, ERROR, GETSTRUCT, heap_close, heap_freetuple(), heap_open(), HeapTupleIsValid, ObjectIdGetDatum, PartitionedRelationId, PARTRELID, RowExclusiveLock, SearchSysCacheCopy1, and HeapTupleData::t_self.

Referenced by ATExecAttachPartition(), ATExecDetachPartition(), DefineRelation(), and heap_drop_with_catalog().

2847 {
2848  HeapTuple tuple;
2849  Relation pg_partitioned_table;
2850  Form_pg_partitioned_table part_table_form;
2851 
2852  pg_partitioned_table = heap_open(PartitionedRelationId, RowExclusiveLock);
2853 
2854  tuple = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(parentId));
2855 
2856  if (!HeapTupleIsValid(tuple))
2857  elog(ERROR, "cache lookup failed for partition key of relation %u",
2858  parentId);
2859 
2860  part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
2861  part_table_form->partdefid = defaultPartId;
2862  CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple);
2863 
2864  heap_freetuple(tuple);
2865  heap_close(pg_partitioned_table, RowExclusiveLock);
2866 }
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
#define heap_close(r, l)
Definition: heapam.h:97
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1373
FormData_pg_partitioned_table * Form_pg_partitioned_table
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
#define ERROR
Definition: elog.h:43
ItemPointerData t_self
Definition: htup.h:65
#define PartitionedRelationId
#define RowExclusiveLock
Definition: lockdefs.h:38
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:210
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:173
#define elog
Definition: elog.h:219