PostgreSQL Source Code  git master
partition.c File Reference
#include "postgres.h"
#include "access/hash.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 "parser/parse_coerce.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/hashutils.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  PartitionHashBound
 
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 PartitionHashBound PartitionHashBound
 
typedef struct PartitionListValue PartitionListValue
 
typedef struct PartitionRangeBound PartitionRangeBound
 

Functions

static int32 qsort_partition_hbound_cmp (const void *a, const void *b)
 
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_hash (Relation parent, PartitionBoundSpec *spec)
 
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_hbound_cmp (int modulus1, int remainder1, int modulus2, int remainder2)
 
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 int get_partition_bound_num_indexes (PartitionBoundInfo b)
 
static int get_greatest_modulus (PartitionBoundInfo b)
 
static uint64 compute_hash_value (PartitionKey key, Datum *values, bool *isnull)
 
 PG_FUNCTION_INFO_V1 (satisfies_hash_partition)
 
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)
 
int get_partition_for_tuple (Relation relation, Datum *values, bool *isnull)
 
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)
 
Datum satisfies_hash_partition (PG_FUNCTION_ARGS)
 

Macro Definition Documentation

◆ partition_bound_accepts_nulls

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

◆ partition_bound_has_default

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

Definition at line 110 of file partition.c.

Referenced by check_new_partition_bound(), and get_default_oid_from_partdesc().

Typedef Documentation

◆ PartitionBoundInfoData

◆ PartitionHashBound

◆ PartitionListValue

◆ PartitionRangeBound

Function Documentation

◆ check_default_allows_bound()

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

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

1220 {
1221  List *new_part_constraints;
1222  List *def_part_constraints;
1223  List *all_parts;
1224  ListCell *lc;
1225 
1226  new_part_constraints = (new_spec->strategy == PARTITION_STRATEGY_LIST)
1227  ? get_qual_for_list(parent, new_spec)
1228  : get_qual_for_range(parent, new_spec, false);
1229  def_part_constraints =
1230  get_proposed_default_constraint(new_part_constraints);
1231 
1232  /*
1233  * If the existing constraints on the default partition imply that it will
1234  * not contain any row that would belong to the new partition, we can
1235  * avoid scanning the default partition.
1236  */
1237  if (PartConstraintImpliedByRelConstraint(default_rel, def_part_constraints))
1238  {
1239  ereport(INFO,
1240  (errmsg("updated partition constraint for default partition \"%s\" is implied by existing constraints",
1241  RelationGetRelationName(default_rel))));
1242  return;
1243  }
1244 
1245  /*
1246  * Scan the default partition and its subpartitions, and check for rows
1247  * that do not satisfy the revised partition constraints.
1248  */
1249  if (default_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1250  all_parts = find_all_inheritors(RelationGetRelid(default_rel),
1251  AccessExclusiveLock, NULL);
1252  else
1253  all_parts = list_make1_oid(RelationGetRelid(default_rel));
1254 
1255  foreach(lc, all_parts)
1256  {
1257  Oid part_relid = lfirst_oid(lc);
1258  Relation part_rel;
1259  Expr *constr;
1260  Expr *partition_constraint;
1261  EState *estate;
1262  HeapTuple tuple;
1263  ExprState *partqualstate = NULL;
1264  Snapshot snapshot;
1265  TupleDesc tupdesc;
1266  ExprContext *econtext;
1267  HeapScanDesc scan;
1268  MemoryContext oldCxt;
1269  TupleTableSlot *tupslot;
1270 
1271  /* Lock already taken above. */
1272  if (part_relid != RelationGetRelid(default_rel))
1273  {
1274  part_rel = heap_open(part_relid, NoLock);
1275 
1276  /*
1277  * If the partition constraints on default partition child imply
1278  * that it will not contain any row that would belong to the new
1279  * partition, we can avoid scanning the child table.
1280  */
1282  def_part_constraints))
1283  {
1284  ereport(INFO,
1285  (errmsg("updated partition constraint for default partition \"%s\" is implied by existing constraints",
1286  RelationGetRelationName(part_rel))));
1287 
1288  heap_close(part_rel, NoLock);
1289  continue;
1290  }
1291  }
1292  else
1293  part_rel = default_rel;
1294 
1295  /*
1296  * Only RELKIND_RELATION relations (i.e. leaf partitions) need to be
1297  * scanned.
1298  */
1299  if (part_rel->rd_rel->relkind != RELKIND_RELATION)
1300  {
1301  if (part_rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1302  ereport(WARNING,
1303  (errcode(ERRCODE_CHECK_VIOLATION),
1304  errmsg("skipped scanning foreign table \"%s\" which is a partition of default partition \"%s\"",
1305  RelationGetRelationName(part_rel),
1306  RelationGetRelationName(default_rel))));
1307 
1308  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
1309  heap_close(part_rel, NoLock);
1310 
1311  continue;
1312  }
1313 
1314  tupdesc = CreateTupleDescCopy(RelationGetDescr(part_rel));
1315  constr = linitial(def_part_constraints);
1316  partition_constraint = (Expr *)
1317  map_partition_varattnos((List *) constr,
1318  1, part_rel, parent, NULL);
1319  estate = CreateExecutorState();
1320 
1321  /* Build expression execution states for partition check quals */
1322  partqualstate = ExecPrepareExpr(partition_constraint, estate);
1323 
1324  econtext = GetPerTupleExprContext(estate);
1325  snapshot = RegisterSnapshot(GetLatestSnapshot());
1326  scan = heap_beginscan(part_rel, snapshot, 0, NULL);
1327  tupslot = MakeSingleTupleTableSlot(tupdesc);
1328 
1329  /*
1330  * Switch to per-tuple memory context and reset it for each tuple
1331  * produced, so we don't leak memory.
1332  */
1334 
1335  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1336  {
1337  ExecStoreTuple(tuple, tupslot, InvalidBuffer, false);
1338  econtext->ecxt_scantuple = tupslot;
1339 
1340  if (!ExecCheck(partqualstate, econtext))
1341  ereport(ERROR,
1342  (errcode(ERRCODE_CHECK_VIOLATION),
1343  errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
1344  RelationGetRelationName(default_rel))));
1345 
1346  ResetExprContext(econtext);
1348  }
1349 
1350  MemoryContextSwitchTo(oldCxt);
1351  heap_endscan(scan);
1352  UnregisterSnapshot(snapshot);
1354  FreeExecutorState(estate);
1355 
1356  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
1357  heap_close(part_rel, NoLock); /* keep the lock until commit */
1358  }
1359 }
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:437
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:1461
void FreeExecutorState(EState *estate)
Definition: execUtils.c:186
#define GetPerTupleExprContext(estate)
Definition: executor.h:467
#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:1752
#define NoLock
Definition: lockdefs.h:34
List * get_proposed_default_constraint(List *new_part_constraints)
Definition: partition.c:2990
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:216
#define RelationGetRelationName(relation)
Definition: rel.h:445
#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:2067
EState * CreateExecutorState(void)
Definition: execUtils.c:81
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:198
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:788
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:379
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:472
#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:425
#define ResetExprContext(econtext)
Definition: executor.h:461
#define lfirst_oid(lc)
Definition: pg_list.h:108
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:13628

◆ check_new_partition_bound()

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

Definition at line 945 of file partition.c.

References Assert, PartitionDescData::boundinfo, castNode, Const::constisnull, Const::constvalue, DatumGetInt32, PartitionBoundInfoData::datums, PartitionRangeBound::datums, PartitionBoundInfoData::default_index, elog, equal(), ereport, errcode(), errdetail(), errmsg(), ERROR, get_greatest_modulus(), 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(), PartitionBoundSpec::modulus, 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_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, RelationGetPartitionDesc, RelationGetPartitionKey, PartitionBoundSpec::remainder, PartitionKeyData::strategy, PartitionBoundInfoData::strategy, PartitionBoundSpec::strategy, upper(), PartitionBoundSpec::upperdatums, and val.

Referenced by ATExecAttachPartition(), and DefineRelation().

947 {
949  PartitionDesc partdesc = RelationGetPartitionDesc(parent);
950  PartitionBoundInfo boundinfo = partdesc->boundinfo;
951  ParseState *pstate = make_parsestate(NULL);
952  int with = -1;
953  bool overlap = false;
954 
955  if (spec->is_default)
956  {
957  if (boundinfo == NULL || !partition_bound_has_default(boundinfo))
958  return;
959 
960  /* Default partition already exists, error out. */
961  ereport(ERROR,
962  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
963  errmsg("partition \"%s\" conflicts with existing default partition \"%s\"",
964  relname, get_rel_name(partdesc->oids[boundinfo->default_index])),
965  parser_errposition(pstate, spec->location)));
966  }
967 
968  switch (key->strategy)
969  {
971  {
973  Assert(spec->remainder >= 0 && spec->remainder < spec->modulus);
974 
975  if (partdesc->nparts > 0)
976  {
977  PartitionBoundInfo boundinfo = partdesc->boundinfo;
978  Datum **datums = boundinfo->datums;
979  int ndatums = boundinfo->ndatums;
980  int greatest_modulus;
981  int remainder;
982  int offset;
983  bool equal,
984  valid_modulus = true;
985  int prev_modulus, /* Previous largest modulus */
986  next_modulus; /* Next largest modulus */
987 
988  /*
989  * Check rule that every modulus must be a factor of the
990  * next larger modulus. For example, if you have a bunch
991  * of partitions that all have modulus 5, you can add a
992  * new partition with modulus 10 or a new partition with
993  * modulus 15, but you cannot add both a partition with
994  * modulus 10 and a partition with modulus 15, because 10
995  * is not a factor of 15.
996  *
997  * Get greatest bound in array boundinfo->datums which is
998  * less than or equal to spec->modulus and
999  * spec->remainder.
1000  */
1001  offset = partition_bound_bsearch(key, boundinfo, spec,
1002  true, &equal);
1003  if (offset < 0)
1004  {
1005  next_modulus = DatumGetInt32(datums[0][0]);
1006  valid_modulus = (next_modulus % spec->modulus) == 0;
1007  }
1008  else
1009  {
1010  prev_modulus = DatumGetInt32(datums[offset][0]);
1011  valid_modulus = (spec->modulus % prev_modulus) == 0;
1012 
1013  if (valid_modulus && (offset + 1) < ndatums)
1014  {
1015  next_modulus = DatumGetInt32(datums[offset + 1][0]);
1016  valid_modulus = (next_modulus % spec->modulus) == 0;
1017  }
1018  }
1019 
1020  if (!valid_modulus)
1021  ereport(ERROR,
1022  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1023  errmsg("every hash partition modulus must be a factor of the next larger modulus")));
1024 
1025  greatest_modulus = get_greatest_modulus(boundinfo);
1026  remainder = spec->remainder;
1027 
1028  /*
1029  * Normally, the lowest remainder that could conflict with
1030  * the new partition is equal to the remainder specified
1031  * for the new partition, but when the new partition has a
1032  * modulus higher than any used so far, we need to adjust.
1033  */
1034  if (remainder >= greatest_modulus)
1035  remainder = remainder % greatest_modulus;
1036 
1037  /* Check every potentially-conflicting remainder. */
1038  do
1039  {
1040  if (boundinfo->indexes[remainder] != -1)
1041  {
1042  overlap = true;
1043  with = boundinfo->indexes[remainder];
1044  break;
1045  }
1046  remainder += spec->modulus;
1047  } while (remainder < greatest_modulus);
1048  }
1049 
1050  break;
1051  }
1052 
1054  {
1056 
1057  if (partdesc->nparts > 0)
1058  {
1059  ListCell *cell;
1060 
1061  Assert(boundinfo &&
1062  boundinfo->strategy == PARTITION_STRATEGY_LIST &&
1063  (boundinfo->ndatums > 0 ||
1064  partition_bound_accepts_nulls(boundinfo) ||
1065  partition_bound_has_default(boundinfo)));
1066 
1067  foreach(cell, spec->listdatums)
1068  {
1069  Const *val = castNode(Const, lfirst(cell));
1070 
1071  if (!val->constisnull)
1072  {
1073  int offset;
1074  bool equal;
1075 
1076  offset = partition_bound_bsearch(key, boundinfo,
1077  &val->constvalue,
1078  true, &equal);
1079  if (offset >= 0 && equal)
1080  {
1081  overlap = true;
1082  with = boundinfo->indexes[offset];
1083  break;
1084  }
1085  }
1086  else if (partition_bound_accepts_nulls(boundinfo))
1087  {
1088  overlap = true;
1089  with = boundinfo->null_index;
1090  break;
1091  }
1092  }
1093  }
1094 
1095  break;
1096  }
1097 
1099  {
1101  *upper;
1102 
1104  lower = make_one_range_bound(key, -1, spec->lowerdatums, true);
1105  upper = make_one_range_bound(key, -1, spec->upperdatums, false);
1106 
1107  /*
1108  * First check if the resulting range would be empty with
1109  * specified lower and upper bounds
1110  */
1111  if (partition_rbound_cmp(key, lower->datums, lower->kind, true,
1112  upper) >= 0)
1113  {
1114  ereport(ERROR,
1115  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1116  errmsg("empty range bound specified for partition \"%s\"",
1117  relname),
1118  errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
1121  parser_errposition(pstate, spec->location)));
1122  }
1123 
1124  if (partdesc->nparts > 0)
1125  {
1126  PartitionBoundInfo boundinfo = partdesc->boundinfo;
1127  int offset;
1128  bool equal;
1129 
1130  Assert(boundinfo &&
1131  boundinfo->strategy == PARTITION_STRATEGY_RANGE &&
1132  (boundinfo->ndatums > 0 ||
1133  partition_bound_has_default(boundinfo)));
1134 
1135  /*
1136  * Test whether the new lower bound (which is treated
1137  * inclusively as part of the new partition) lies inside
1138  * an existing partition, or in a gap.
1139  *
1140  * If it's inside an existing partition, the bound at
1141  * offset + 1 will be the upper bound of that partition,
1142  * and its index will be >= 0.
1143  *
1144  * If it's in a gap, the bound at offset + 1 will be the
1145  * lower bound of the next partition, and its index will
1146  * be -1. This is also true if there is no next partition,
1147  * since the index array is initialised with an extra -1
1148  * at the end.
1149  */
1150  offset = partition_bound_bsearch(key, boundinfo, lower,
1151  true, &equal);
1152 
1153  if (boundinfo->indexes[offset + 1] < 0)
1154  {
1155  /*
1156  * Check that the new partition will fit in the gap.
1157  * For it to fit, the new upper bound must be less
1158  * than or equal to the lower bound of the next
1159  * partition, if there is one.
1160  */
1161  if (offset + 1 < boundinfo->ndatums)
1162  {
1163  int32 cmpval;
1164 
1165  cmpval = partition_bound_cmp(key, boundinfo,
1166  offset + 1, upper,
1167  true);
1168  if (cmpval < 0)
1169  {
1170  /*
1171  * The new partition overlaps with the
1172  * existing partition between offset + 1 and
1173  * offset + 2.
1174  */
1175  overlap = true;
1176  with = boundinfo->indexes[offset + 2];
1177  }
1178  }
1179  }
1180  else
1181  {
1182  /*
1183  * The new partition overlaps with the existing
1184  * partition between offset and offset + 1.
1185  */
1186  overlap = true;
1187  with = boundinfo->indexes[offset + 1];
1188  }
1189  }
1190 
1191  break;
1192  }
1193 
1194  default:
1195  elog(ERROR, "unexpected partition strategy: %d",
1196  (int) key->strategy);
1197  }
1198 
1199  if (overlap)
1200  {
1201  Assert(with >= 0);
1202  ereport(ERROR,
1203  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1204  errmsg("partition \"%s\" would overlap partition \"%s\"",
1205  relname, get_rel_name(partdesc->oids[with])),
1206  parser_errposition(pstate, spec->location)));
1207  }
1208 }
Datum constvalue
Definition: primnodes.h:196
PartitionRangeDatumKind * kind
Definition: partition.c:137
#define DatumGetInt32(X)
Definition: postgres.h:478
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2984
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:43
static int get_greatest_modulus(PartitionBoundInfo b)
Definition: partition.c:3060
#define castNode(_type_, nodeptr)
Definition: nodes.h:581
#define partition_bound_accepts_nulls(bi)
Definition: partition.c:109
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:284
PartitionBoundInfo boundinfo
Definition: partition.h:40
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:110
static int partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo, void *probe, bool probe_is_bound, bool *is_equal)
Definition: partition.c:2880
static int32 partition_rbound_cmp(PartitionKey key, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partition.c:2720
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:10981
static PartitionRangeBound * make_one_range_bound(PartitionKey key, int index, List *datums, bool lower)
Definition: partition.c:2657
uintptr_t Datum
Definition: postgres.h:372
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:787
#define Assert(condition)
Definition: c.h:670
#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:788
#define RelationGetPartitionKey(relation)
Definition: rel.h:593
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:789
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define elog
Definition: elog.h:219
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1745
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:2810
bool constisnull
Definition: primnodes.h:197
#define RelationGetPartitionDesc(relation)
Definition: rel.h:641

◆ compute_hash_value()

static uint64 compute_hash_value ( PartitionKey  key,
Datum values,
bool isnull 
)
static

Definition at line 3075 of file partition.c.

References Assert, DatumGetUInt64, FmgrInfo::fn_oid, FunctionCall2, hash(), hash_combine64(), HASH_PARTITION_SEED, i, OidIsValid, PartitionKeyData::partnatts, PartitionKeyData::partsupfunc, and UInt64GetDatum.

Referenced by get_partition_for_tuple().

3076 {
3077  int i;
3078  int nkeys = key->partnatts;
3079  uint64 rowHash = 0;
3081 
3082  for (i = 0; i < nkeys; i++)
3083  {
3084  if (!isnull[i])
3085  {
3086  Datum hash;
3087 
3088  Assert(OidIsValid(key->partsupfunc[i].fn_oid));
3089 
3090  /*
3091  * Compute hash for each datum value by calling respective
3092  * datatype-specific hash functions of each partition key
3093  * attribute.
3094  */
3095  hash = FunctionCall2(&key->partsupfunc[i], values[i], seed);
3096 
3097  /* Form a single 64-bit hash value */
3098  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
3099  }
3100  }
3101 
3102  return rowHash;
3103 }
FmgrInfo * partsupfunc
Definition: rel.h:63
#define UInt64GetDatum(X)
Definition: postgres.h:654
#define FunctionCall2(flinfo, arg1, arg2)
Definition: fmgr.h:605
#define OidIsValid(objectId)
Definition: c.h:576
static uint64 hash_combine64(uint64 a, uint64 b)
Definition: hashutils.h:29
uintptr_t Datum
Definition: postgres.h:372
int16 partnatts
Definition: rel.h:55
Oid fn_oid
Definition: fmgr.h:59
#define DatumGetUInt64(X)
Definition: postgres.h:640
#define Assert(condition)
Definition: c.h:670
#define HASH_PARTITION_SEED
Definition: partition.h:23
static Datum values[MAXATTR]
Definition: bootstrap.c:164
int i
static unsigned hash(unsigned *uv, int n)
Definition: rege_dfa.c:541

◆ generate_partition_qual()

static List * generate_partition_qual ( Relation  rel)
static

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

2429 {
2430  HeapTuple tuple;
2431  MemoryContext oldcxt;
2432  Datum boundDatum;
2433  bool isnull;
2434  PartitionBoundSpec *bound;
2435  List *my_qual = NIL,
2436  *result = NIL;
2437  Relation parent;
2438  bool found_whole_row;
2439 
2440  /* Guard against stack overflow due to overly deep partition tree */
2442 
2443  /* Quick copy */
2444  if (rel->rd_partcheck != NIL)
2445  return copyObject(rel->rd_partcheck);
2446 
2447  /* Grab at least an AccessShareLock on the parent table */
2449  AccessShareLock);
2450 
2451  /* Get pg_class.relpartbound */
2452  tuple = SearchSysCache1(RELOID, RelationGetRelid(rel));
2453  if (!HeapTupleIsValid(tuple))
2454  elog(ERROR, "cache lookup failed for relation %u",
2455  RelationGetRelid(rel));
2456 
2457  boundDatum = SysCacheGetAttr(RELOID, tuple,
2459  &isnull);
2460  if (isnull) /* should not happen */
2461  elog(ERROR, "relation \"%s\" has relpartbound = null",
2463  bound = castNode(PartitionBoundSpec,
2464  stringToNode(TextDatumGetCString(boundDatum)));
2465  ReleaseSysCache(tuple);
2466 
2467  my_qual = get_qual_from_partbound(rel, parent, bound);
2468 
2469  /* Add the parent's quals to the list (if any) */
2470  if (parent->rd_rel->relispartition)
2471  result = list_concat(generate_partition_qual(parent), my_qual);
2472  else
2473  result = my_qual;
2474 
2475  /*
2476  * Change Vars to have partition's attnos instead of the parent's. We do
2477  * this after we concatenate the parent's quals, because we want every Var
2478  * in it to bear this relation's attnos. It's safe to assume varno = 1
2479  * here.
2480  */
2481  result = map_partition_varattnos(result, 1, rel, parent,
2482  &found_whole_row);
2483  /* There can never be a whole-row reference here */
2484  if (found_whole_row)
2485  elog(ERROR, "unexpected whole-row reference found in partition key");
2486 
2487  /* Save a copy in the relcache */
2489  rel->rd_partcheck = copyObject(result);
2490  MemoryContextSwitchTo(oldcxt);
2491 
2492  /* Keep the parent locked until commit */
2493  heap_close(parent, NoLock);
2494 
2495  return result;
2496 }
#define NIL
Definition: pg_list.h:69
void * stringToNode(char *str)
Definition: read.c:38
#define castNode(_type_, nodeptr)
Definition: nodes.h:581
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:1413
List * map_partition_varattnos(List *expr, int target_varno, Relation partrel, Relation parent, bool *found_whole_row)
Definition: partition.c:1461
#define ERROR
Definition: elog.h:43
Oid get_partition_parent(Oid relid)
Definition: partition.c:1371
#define NoLock
Definition: lockdefs.h:34
void check_stack_depth(void)
Definition: postgres.c:3150
#define RelationGetRelationName(relation)
Definition: rel.h:445
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:2428
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:625
Definition: pg_list.h:45
#define RelationGetRelid(relation)
Definition: rel.h:425
List * rd_partcheck
Definition: rel.h:132
MemoryContext CacheMemoryContext
Definition: mcxt.c:46

◆ get_default_oid_from_partdesc()

Oid get_default_oid_from_partdesc ( PartitionDesc  partdesc)

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

2919 {
2920  if (partdesc && partdesc->boundinfo &&
2922  return partdesc->oids[partdesc->boundinfo->default_index];
2923 
2924  return InvalidOid;
2925 }
PartitionBoundInfo boundinfo
Definition: partition.h:40
#define partition_bound_has_default(bi)
Definition: partition.c:110
#define InvalidOid
Definition: postgres_ext.h:36

◆ get_default_partition_oid()

Oid get_default_partition_oid ( Oid  parentId)

Definition at line 2935 of file partition.c.

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

Referenced by heap_drop_with_catalog(), and RelationBuildPartitionDesc().

2936 {
2937  HeapTuple tuple;
2938  Oid defaultPartId = InvalidOid;
2939 
2940  tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(parentId));
2941 
2942  if (HeapTupleIsValid(tuple))
2943  {
2944  Form_pg_partitioned_table part_table_form;
2945 
2946  part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
2947  defaultPartId = part_table_form->partdefid;
2948  ReleaseSysCache(tuple);
2949  }
2950 
2951  return defaultPartId;
2952 }
#define GETSTRUCT(TUP)
Definition: htup_details.h:661
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

◆ get_greatest_modulus()

static int get_greatest_modulus ( PartitionBoundInfo  b)
static

Definition at line 3060 of file partition.c.

References Assert, DatumGetInt32, PartitionBoundInfoData::datums, PartitionBoundInfoData::ndatums, PARTITION_STRATEGY_HASH, and PartitionBoundInfoData::strategy.

Referenced by check_new_partition_bound(), get_partition_bound_num_indexes(), get_partition_for_tuple(), and partition_bounds_equal().

3061 {
3062  Assert(bound && bound->strategy == PARTITION_STRATEGY_HASH);
3063  Assert(bound->datums && bound->ndatums > 0);
3064  Assert(DatumGetInt32(bound->datums[bound->ndatums - 1][0]) > 0);
3065 
3066  return DatumGetInt32(bound->datums[bound->ndatums - 1][0]);
3067 }
#define DatumGetInt32(X)
Definition: postgres.h:478
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:787
#define Assert(condition)
Definition: c.h:670

◆ get_partition_bound_num_indexes()

static int get_partition_bound_num_indexes ( PartitionBoundInfo  b)
static

Definition at line 3018 of file partition.c.

References Assert, elog, ERROR, get_greatest_modulus(), PartitionBoundInfoData::ndatums, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, and PartitionBoundInfoData::strategy.

Referenced by partition_bounds_copy().

3019 {
3020  int num_indexes;
3021 
3022  Assert(bound);
3023 
3024  switch (bound->strategy)
3025  {
3027 
3028  /*
3029  * The number of the entries in the indexes array is same as the
3030  * greatest modulus.
3031  */
3032  num_indexes = get_greatest_modulus(bound);
3033  break;
3034 
3036  num_indexes = bound->ndatums;
3037  break;
3038 
3040  /* Range partitioned table has an extra index. */
3041  num_indexes = bound->ndatums + 1;
3042  break;
3043 
3044  default:
3045  elog(ERROR, "unexpected partition strategy: %d",
3046  (int) bound->strategy);
3047  }
3048 
3049  return num_indexes;
3050 }
static int get_greatest_modulus(PartitionBoundInfo b)
Definition: partition.c:3060
#define ERROR
Definition: elog.h:43
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:787
#define Assert(condition)
Definition: c.h:670
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:788
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:789
#define elog
Definition: elog.h:219

◆ get_partition_for_tuple()

int get_partition_for_tuple ( Relation  relation,
Datum values,
bool isnull 
)

Definition at line 2507 of file partition.c.

References PartitionDescData::boundinfo, compute_hash_value(), PartitionBoundInfoData::default_index, elog, equal(), ERROR, get_greatest_modulus(), i, PartitionBoundInfoData::indexes, PartitionBoundInfoData::null_index, partition_bound_accepts_nulls, partition_bound_bsearch(), PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionKeyData::partnatts, RelationGetPartitionDesc, RelationGetPartitionKey, and PartitionKeyData::strategy.

Referenced by ExecFindPartition().

2508 {
2509  int bound_offset;
2510  int part_index = -1;
2511  PartitionKey key = RelationGetPartitionKey(relation);
2512  PartitionDesc partdesc = RelationGetPartitionDesc(relation);
2513 
2514  /* Route as appropriate based on partitioning strategy. */
2515  switch (key->strategy)
2516  {
2518  {
2519  PartitionBoundInfo boundinfo = partdesc->boundinfo;
2520  int greatest_modulus = get_greatest_modulus(boundinfo);
2521  uint64 rowHash = compute_hash_value(key, values, isnull);
2522 
2523  part_index = boundinfo->indexes[rowHash % greatest_modulus];
2524  }
2525  break;
2526 
2528  if (isnull[0])
2529  {
2531  part_index = partdesc->boundinfo->null_index;
2532  }
2533  else
2534  {
2535  bool equal = false;
2536 
2537  bound_offset = partition_bound_bsearch(key,
2538  partdesc->boundinfo,
2539  values,
2540  false,
2541  &equal);
2542  if (bound_offset >= 0 && equal)
2543  part_index = partdesc->boundinfo->indexes[bound_offset];
2544  }
2545  break;
2546 
2548  {
2549  bool equal = false,
2550  range_partkey_has_null = false;
2551  int i;
2552 
2553  /*
2554  * No range includes NULL, so this will be accepted by the
2555  * default partition if there is one, and otherwise rejected.
2556  */
2557  for (i = 0; i < key->partnatts; i++)
2558  {
2559  if (isnull[i])
2560  {
2561  range_partkey_has_null = true;
2562  break;
2563  }
2564  }
2565 
2566  if (!range_partkey_has_null)
2567  {
2568  bound_offset = partition_bound_bsearch(key,
2569  partdesc->boundinfo,
2570  values,
2571  false,
2572  &equal);
2573 
2574  /*
2575  * The bound at bound_offset is less than or equal to the
2576  * tuple value, so the bound at offset+1 is the upper
2577  * bound of the partition we're looking for, if there
2578  * actually exists one.
2579  */
2580  part_index = partdesc->boundinfo->indexes[bound_offset + 1];
2581  }
2582  }
2583  break;
2584 
2585  default:
2586  elog(ERROR, "unexpected partition strategy: %d",
2587  (int) key->strategy);
2588  }
2589 
2590  /*
2591  * part_index < 0 means we failed to find a partition of this parent. Use
2592  * the default partition, if there is one.
2593  */
2594  if (part_index < 0)
2595  part_index = partdesc->boundinfo->default_index;
2596 
2597  return part_index;
2598 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2984
static int get_greatest_modulus(PartitionBoundInfo b)
Definition: partition.c:3060
#define partition_bound_accepts_nulls(bi)
Definition: partition.c:109
char strategy
Definition: rel.h:54
PartitionBoundInfo boundinfo
Definition: partition.h:40
#define ERROR
Definition: elog.h:43
static int partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo, void *probe, bool probe_is_bound, bool *is_equal)
Definition: partition.c:2880
int16 partnatts
Definition: rel.h:55
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:787
static uint64 compute_hash_value(PartitionKey key, Datum *values, bool *isnull)
Definition: partition.c:3075
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:788
#define RelationGetPartitionKey(relation)
Definition: rel.h:593
static Datum values[MAXATTR]
Definition: bootstrap.c:164
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:789
int i
#define elog
Definition: elog.h:219
#define RelationGetPartitionDesc(relation)
Definition: rel.h:641

◆ get_partition_operator()

static Oid get_partition_operator ( PartitionKey  key,
int  col,
StrategyNumber  strategy,
bool need_relabel 
)
static

Definition at line 1545 of file partition.c.

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

Referenced by make_partition_op_expr().

1547 {
1548  Oid operoid;
1549 
1550  /*
1551  * First check if there exists an operator of the given strategy, with
1552  * this column's type as both its lefttype and righttype, in the
1553  * partitioning operator family specified for the column.
1554  */
1555  operoid = get_opfamily_member(key->partopfamily[col],
1556  key->parttypid[col],
1557  key->parttypid[col],
1558  strategy);
1559 
1560  /*
1561  * If one doesn't exist, we must resort to using an operator in the same
1562  * operator family but with the operator class declared input type. It is
1563  * OK to do so, because the column's type is known to be binary-coercible
1564  * with the operator class input type (otherwise, the operator class in
1565  * question would not have been accepted as the partitioning operator
1566  * class). We must however inform the caller to wrap the non-Const
1567  * expression with a RelabelType node to denote the implicit coercion. It
1568  * ensures that the resulting expression structurally matches similarly
1569  * processed expressions within the optimizer.
1570  */
1571  if (!OidIsValid(operoid))
1572  {
1573  operoid = get_opfamily_member(key->partopfamily[col],
1574  key->partopcintype[col],
1575  key->partopcintype[col],
1576  strategy);
1577  if (!OidIsValid(operoid))
1578  elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
1579  strategy, key->partopcintype[col], key->partopcintype[col],
1580  key->partopfamily[col]);
1581  *need_relabel = true;
1582  }
1583  else
1584  *need_relabel = false;
1585 
1586  return operoid;
1587 }
Oid * partopfamily
Definition: rel.h:61
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:576
#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

◆ get_partition_parent()

Oid get_partition_parent ( Oid  relid)

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

1372 {
1373  Form_pg_inherits form;
1374  Relation catalogRelation;
1375  SysScanDesc scan;
1376  ScanKeyData key[2];
1377  HeapTuple tuple;
1378  Oid result;
1379 
1380  catalogRelation = heap_open(InheritsRelationId, AccessShareLock);
1381 
1382  ScanKeyInit(&key[0],
1384  BTEqualStrategyNumber, F_OIDEQ,
1385  ObjectIdGetDatum(relid));
1386  ScanKeyInit(&key[1],
1388  BTEqualStrategyNumber, F_INT4EQ,
1389  Int32GetDatum(1));
1390 
1391  scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId, true,
1392  NULL, 2, key);
1393 
1394  tuple = systable_getnext(scan);
1395  if (!HeapTupleIsValid(tuple))
1396  elog(ERROR, "could not find tuple for parent of relation %u", relid);
1397 
1398  form = (Form_pg_inherits) GETSTRUCT(tuple);
1399  result = form->inhparent;
1400 
1401  systable_endscan(scan);
1402  heap_close(catalogRelation, AccessShareLock);
1403 
1404  return result;
1405 }
#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:661
#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

◆ get_partition_qual_relid()

Expr* get_partition_qual_relid ( Oid  relid)

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

1512 {
1513  Relation rel = heap_open(relid, AccessShareLock);
1514  Expr *result = NULL;
1515  List *and_args;
1516 
1517  /* Do the work only if this relation is a partition. */
1518  if (rel->rd_rel->relispartition)
1519  {
1520  and_args = generate_partition_qual(rel);
1521 
1522  if (and_args == NIL)
1523  result = NULL;
1524  else if (list_length(and_args) > 1)
1525  result = makeBoolExpr(AND_EXPR, and_args, -1);
1526  else
1527  result = linitial(and_args);
1528  }
1529 
1530  /* Keep the lock. */
1531  heap_close(rel, NoLock);
1532 
1533  return result;
1534 }
#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:368
#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:2428
Definition: pg_list.h:45

◆ get_proposed_default_constraint()

List* get_proposed_default_constraint ( List new_part_constraints)

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

2991 {
2992  Expr *defPartConstraint;
2993 
2994  defPartConstraint = make_ands_explicit(new_part_constraints);
2995 
2996  /*
2997  * Derive the partition constraints of default partition by negating the
2998  * given partition constraints. The partition constraint never evaluates
2999  * to NULL, so negating it like this is safe.
3000  */
3001  defPartConstraint = makeBoolExpr(NOT_EXPR,
3002  list_make1(defPartConstraint),
3003  -1);
3004  defPartConstraint =
3005  (Expr *) eval_const_expressions(NULL,
3006  (Node *) defPartConstraint);
3007  defPartConstraint = canonicalize_qual(defPartConstraint);
3008 
3009  return list_make1(defPartConstraint);
3010 }
Definition: nodes.h:512
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2459
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:368
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

◆ get_qual_for_hash()

static List * get_qual_for_hash ( Relation  parent,
PartitionBoundSpec spec 
)
static

Definition at line 1669 of file partition.c.

References generate_unaccent_rules::args, BOOLOID, COERCE_EXPLICIT_CALL, copyObject, i, Int32GetDatum, INT4OID, InvalidOid, lappend(), lfirst, list_head(), list_make1, list_make3, lnext, makeConst(), makeFuncExpr(), makeVar(), PartitionBoundSpec::modulus, ObjectIdGetDatum, OIDOID, PartitionKeyData::partattrs, PartitionKeyData::partexprs, PartitionKeyData::partnatts, PartitionKeyData::parttypcoll, PartitionKeyData::parttypid, PartitionKeyData::parttypmod, RelationGetPartitionKey, RelationGetRelid, and PartitionBoundSpec::remainder.

Referenced by get_qual_from_partbound().

1670 {
1671  PartitionKey key = RelationGetPartitionKey(parent);
1672  FuncExpr *fexpr;
1673  Node *relidConst;
1674  Node *modulusConst;
1675  Node *remainderConst;
1676  List *args;
1677  ListCell *partexprs_item;
1678  int i;
1679 
1680  /* Fixed arguments. */
1681  relidConst = (Node *) makeConst(OIDOID,
1682  -1,
1683  InvalidOid,
1684  sizeof(Oid),
1686  false,
1687  true);
1688 
1689  modulusConst = (Node *) makeConst(INT4OID,
1690  -1,
1691  InvalidOid,
1692  sizeof(int32),
1693  Int32GetDatum(spec->modulus),
1694  false,
1695  true);
1696 
1697  remainderConst = (Node *) makeConst(INT4OID,
1698  -1,
1699  InvalidOid,
1700  sizeof(int32),
1701  Int32GetDatum(spec->remainder),
1702  false,
1703  true);
1704 
1705  args = list_make3(relidConst, modulusConst, remainderConst);
1706  partexprs_item = list_head(key->partexprs);
1707 
1708  /* Add an argument for each key column. */
1709  for (i = 0; i < key->partnatts; i++)
1710  {
1711  Node *keyCol;
1712 
1713  /* Left operand */
1714  if (key->partattrs[i] != 0)
1715  {
1716  keyCol = (Node *) makeVar(1,
1717  key->partattrs[i],
1718  key->parttypid[i],
1719  key->parttypmod[i],
1720  key->parttypcoll[i],
1721  0);
1722  }
1723  else
1724  {
1725  keyCol = (Node *) copyObject(lfirst(partexprs_item));
1726  partexprs_item = lnext(partexprs_item);
1727  }
1728 
1729  args = lappend(args, keyCol);
1730  }
1731 
1732  fexpr = makeFuncExpr(F_SATISFIES_HASH_PARTITION,
1733  BOOLOID,
1734  args,
1735  InvalidOid,
1736  InvalidOid,
1738 
1739  return list_make1(fexpr);
1740 }
#define list_make3(x1, x2, x3)
Definition: pg_list.h:141
#define OIDOID
Definition: pg_type.h:328
#define INT4OID
Definition: pg_type.h:316
Definition: nodes.h:512
List * partexprs
Definition: rel.h:58
unsigned int Oid
Definition: postgres_ext.h:31
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:298
signed int int32
Definition: c.h:284
#define list_make1(x1)
Definition: pg_list.h:139
Oid * parttypcoll
Definition: rel.h:74
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
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
int32 * parttypmod
Definition: rel.h:70
#define InvalidOid
Definition: postgres_ext.h:36
#define lfirst(lc)
Definition: pg_list.h:106
#define BOOLOID
Definition: pg_type.h:288
#define RelationGetPartitionKey(relation)
Definition: rel.h:593
#define Int32GetDatum(X)
Definition: postgres.h:485
int i
#define copyObject(obj)
Definition: nodes.h:625
Definition: pg_list.h:45
#define RelationGetRelid(relation)
Definition: rel.h:425
FuncExpr * makeFuncExpr(Oid funcid, Oid rettype, List *args, Oid funccollid, Oid inputcollid, CoercionForm fformat)
Definition: makefuncs.c:519

◆ get_qual_for_list()

static List * get_qual_for_list ( Relation  parent,
PartitionBoundSpec spec 
)
static

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

1753 {
1754  PartitionKey key = RelationGetPartitionKey(parent);
1755  List *result;
1756  Expr *keyCol;
1757  ArrayExpr *arr;
1758  Expr *opexpr;
1759  NullTest *nulltest;
1760  ListCell *cell;
1761  List *arrelems = NIL;
1762  bool list_has_null = false;
1763 
1764  /*
1765  * Only single-column list partitioning is supported, so we are worried
1766  * only about the partition key with index 0.
1767  */
1768  Assert(key->partnatts == 1);
1769 
1770  /* Construct Var or expression representing the partition column */
1771  if (key->partattrs[0] != 0)
1772  keyCol = (Expr *) makeVar(1,
1773  key->partattrs[0],
1774  key->parttypid[0],
1775  key->parttypmod[0],
1776  key->parttypcoll[0],
1777  0);
1778  else
1779  keyCol = (Expr *) copyObject(linitial(key->partexprs));
1780 
1781  /*
1782  * For default list partition, collect datums for all the partitions. The
1783  * default partition constraint should check that the partition key is
1784  * equal to none of those.
1785  */
1786  if (spec->is_default)
1787  {
1788  int i;
1789  int ndatums = 0;
1790  PartitionDesc pdesc = RelationGetPartitionDesc(parent);
1791  PartitionBoundInfo boundinfo = pdesc->boundinfo;
1792 
1793  if (boundinfo)
1794  {
1795  ndatums = boundinfo->ndatums;
1796 
1797  if (partition_bound_accepts_nulls(boundinfo))
1798  list_has_null = true;
1799  }
1800 
1801  /*
1802  * If default is the only partition, there need not be any partition
1803  * constraint on it.
1804  */
1805  if (ndatums == 0 && !list_has_null)
1806  return NIL;
1807 
1808  for (i = 0; i < ndatums; i++)
1809  {
1810  Const *val;
1811 
1812  /*
1813  * Construct Const from known-not-null datum. We must be careful
1814  * to copy the value, because our result has to be able to outlive
1815  * the relcache entry we're copying from.
1816  */
1817  val = makeConst(key->parttypid[0],
1818  key->parttypmod[0],
1819  key->parttypcoll[0],
1820  key->parttyplen[0],
1821  datumCopy(*boundinfo->datums[i],
1822  key->parttypbyval[0],
1823  key->parttyplen[0]),
1824  false, /* isnull */
1825  key->parttypbyval[0]);
1826 
1827  arrelems = lappend(arrelems, val);
1828  }
1829  }
1830  else
1831  {
1832  /*
1833  * Create list of Consts for the allowed values, excluding any nulls.
1834  */
1835  foreach(cell, spec->listdatums)
1836  {
1837  Const *val = castNode(Const, lfirst(cell));
1838 
1839  if (val->constisnull)
1840  list_has_null = true;
1841  else
1842  arrelems = lappend(arrelems, copyObject(val));
1843  }
1844  }
1845 
1846  if (arrelems)
1847  {
1848  /* Construct an ArrayExpr for the non-null partition values */
1849  arr = makeNode(ArrayExpr);
1850  arr->array_typeid = !type_is_array(key->parttypid[0])
1851  ? get_array_type(key->parttypid[0])
1852  : key->parttypid[0];
1853  arr->array_collid = key->parttypcoll[0];
1854  arr->element_typeid = key->parttypid[0];
1855  arr->elements = arrelems;
1856  arr->multidims = false;
1857  arr->location = -1;
1858 
1859  /* Generate the main expression, i.e., keyCol = ANY (arr) */
1861  keyCol, (Expr *) arr);
1862  }
1863  else
1864  {
1865  /* If there are no partition values, we don't need an = ANY expr */
1866  opexpr = NULL;
1867  }
1868 
1869  if (!list_has_null)
1870  {
1871  /*
1872  * Gin up a "col IS NOT NULL" test that will be AND'd with the main
1873  * expression. This might seem redundant, but the partition routing
1874  * machinery needs it.
1875  */
1876  nulltest = makeNode(NullTest);
1877  nulltest->arg = keyCol;
1878  nulltest->nulltesttype = IS_NOT_NULL;
1879  nulltest->argisrow = false;
1880  nulltest->location = -1;
1881 
1882  result = opexpr ? list_make2(nulltest, opexpr) : list_make1(nulltest);
1883  }
1884  else
1885  {
1886  /*
1887  * Gin up a "col IS NULL" test that will be OR'd with the main
1888  * expression.
1889  */
1890  nulltest = makeNode(NullTest);
1891  nulltest->arg = keyCol;
1892  nulltest->nulltesttype = IS_NULL;
1893  nulltest->argisrow = false;
1894  nulltest->location = -1;
1895 
1896  if (opexpr)
1897  {
1898  Expr *or;
1899 
1900  or = makeBoolExpr(OR_EXPR, list_make2(nulltest, opexpr), -1);
1901  result = list_make1(or);
1902  }
1903  else
1904  result = list_make1(nulltest);
1905  }
1906 
1907  /*
1908  * Note that, in general, applying NOT to a constraint expression doesn't
1909  * necessarily invert the set of rows it accepts, because NOT (NULL) is
1910  * NULL. However, the partition constraints we construct here never
1911  * evaluate to NULL, so applying NOT works as intended.
1912  */
1913  if (spec->is_default)
1914  {
1915  result = list_make1(make_ands_explicit(result));
1916  result = list_make1(makeBoolExpr(NOT_EXPR, result, -1));
1917  }
1918 
1919  return result;
1920 }
#define list_make2(x1, x2)
Definition: pg_list.h:140
bool multidims
Definition: primnodes.h:960
#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:1595
#define castNode(_type_, nodeptr)
Definition: nodes.h:581
Oid get_array_type(Oid typid)
Definition: lsyscache.c:2545
#define partition_bound_accepts_nulls(bi)
Definition: partition.c:109
Oid array_typeid
Definition: primnodes.h:956
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:298
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:368
PartitionBoundInfo boundinfo
Definition: partition.h:40
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:1187
List * elements
Definition: primnodes.h:959
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:961
AttrNumber * partattrs
Definition: rel.h:56
int16 partnatts
Definition: rel.h:55
NullTestType nulltesttype
Definition: primnodes.h:1188
int32 * parttypmod
Definition: rel.h:70
#define makeNode(_type_)
Definition: nodes.h:560
bool * parttypbyval
Definition: rel.h:72
#define Assert(condition)
Definition: c.h:670
#define lfirst(lc)
Definition: pg_list.h:106
int16 * parttyplen
Definition: rel.h:71
Oid array_collid
Definition: primnodes.h:957
int location
Definition: primnodes.h:1190
#define type_is_array(typid)
Definition: lsyscache.h:181
#define RelationGetPartitionKey(relation)
Definition: rel.h:593
Oid element_typeid
Definition: primnodes.h:958
int i
bool argisrow
Definition: primnodes.h:1189
#define copyObject(obj)
Definition: nodes.h:625
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:641

◆ get_qual_for_range()

static List * get_qual_for_range ( Relation  parent,
PartitionBoundSpec spec,
bool  for_default 
)
static

Definition at line 2067 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(), PartitionBoundInfoData::strategy, stringToNode(), SysCacheGetAttr(), TextDatumGetCString, and PartitionBoundSpec::upperdatums.

Referenced by check_default_allows_bound(), and get_qual_from_partbound().

2069 {
2070  List *result = NIL;
2071  ListCell *cell1,
2072  *cell2,
2073  *partexprs_item,
2074  *partexprs_item_saved;
2075  int i,
2076  j;
2077  PartitionRangeDatum *ldatum,
2078  *udatum;
2079  PartitionKey key = RelationGetPartitionKey(parent);
2080  Expr *keyCol;
2081  Const *lower_val,
2082  *upper_val;
2083  List *lower_or_arms,
2084  *upper_or_arms;
2085  int num_or_arms,
2086  current_or_arm;
2087  ListCell *lower_or_start_datum,
2088  *upper_or_start_datum;
2089  bool need_next_lower_arm,
2090  need_next_upper_arm;
2091 
2092  if (spec->is_default)
2093  {
2094  List *or_expr_args = NIL;
2095  PartitionDesc pdesc = RelationGetPartitionDesc(parent);
2096  Oid *inhoids = pdesc->oids;
2097  int nparts = pdesc->nparts,
2098  i;
2099 
2100  for (i = 0; i < nparts; i++)
2101  {
2102  Oid inhrelid = inhoids[i];
2103  HeapTuple tuple;
2104  Datum datum;
2105  bool isnull;
2106  PartitionBoundSpec *bspec;
2107 
2108  tuple = SearchSysCache1(RELOID, inhrelid);
2109  if (!HeapTupleIsValid(tuple))
2110  elog(ERROR, "cache lookup failed for relation %u", inhrelid);
2111 
2112  datum = SysCacheGetAttr(RELOID, tuple,
2114  &isnull);
2115 
2116  Assert(!isnull);
2117  bspec = (PartitionBoundSpec *)
2119  if (!IsA(bspec, PartitionBoundSpec))
2120  elog(ERROR, "expected PartitionBoundSpec");
2121 
2122  if (!bspec->is_default)
2123  {
2124  List *part_qual;
2125 
2126  part_qual = get_qual_for_range(parent, bspec, true);
2127 
2128  /*
2129  * AND the constraints of the partition and add to
2130  * or_expr_args
2131  */
2132  or_expr_args = lappend(or_expr_args, list_length(part_qual) > 1
2133  ? makeBoolExpr(AND_EXPR, part_qual, -1)
2134  : linitial(part_qual));
2135  }
2136  ReleaseSysCache(tuple);
2137  }
2138 
2139  if (or_expr_args != NIL)
2140  {
2141  Expr *other_parts_constr;
2142 
2143  /*
2144  * Combine the constraints obtained for non-default partitions
2145  * using OR. As requested, each of the OR's args doesn't include
2146  * the NOT NULL test for partition keys (which is to avoid its
2147  * useless repetition). Add the same now.
2148  */
2149  other_parts_constr =
2152  list_length(or_expr_args) > 1
2153  ? makeBoolExpr(OR_EXPR, or_expr_args,
2154  -1)
2155  : linitial(or_expr_args)),
2156  -1);
2157 
2158  /*
2159  * Finally, the default partition contains everything *NOT*
2160  * contained in the non-default partitions.
2161  */
2162  result = list_make1(makeBoolExpr(NOT_EXPR,
2163  list_make1(other_parts_constr), -1));
2164  }
2165 
2166  return result;
2167  }
2168 
2169  lower_or_start_datum = list_head(spec->lowerdatums);
2170  upper_or_start_datum = list_head(spec->upperdatums);
2171  num_or_arms = key->partnatts;
2172 
2173  /*
2174  * If it is the recursive call for default, we skip the get_range_nulltest
2175  * to avoid accumulating the NullTest on the same keys for each partition.
2176  */
2177  if (!for_default)
2178  result = get_range_nulltest(key);
2179 
2180  /*
2181  * Iterate over the key columns and check if the corresponding lower and
2182  * upper datums are equal using the btree equality operator for the
2183  * column's type. If equal, we emit single keyCol = common_value
2184  * expression. Starting from the first column for which the corresponding
2185  * lower and upper bound datums are not equal, we generate OR expressions
2186  * as shown in the function's header comment.
2187  */
2188  i = 0;
2189  partexprs_item = list_head(key->partexprs);
2190  partexprs_item_saved = partexprs_item; /* placate compiler */
2191  forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
2192  {
2193  EState *estate;
2194  MemoryContext oldcxt;
2195  Expr *test_expr;
2196  ExprState *test_exprstate;
2197  Datum test_result;
2198  bool isNull;
2199 
2200  ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
2201  udatum = castNode(PartitionRangeDatum, lfirst(cell2));
2202 
2203  /*
2204  * Since get_range_key_properties() modifies partexprs_item, and we
2205  * might need to start over from the previous expression in the later
2206  * part of this function, save away the current value.
2207  */
2208  partexprs_item_saved = partexprs_item;
2209 
2210  get_range_key_properties(key, i, ldatum, udatum,
2211  &partexprs_item,
2212  &keyCol,
2213  &lower_val, &upper_val);
2214 
2215  /*
2216  * If either value is NULL, the corresponding partition bound is
2217  * either MINVALUE or MAXVALUE, and we treat them as unequal, because
2218  * even if they're the same, there is no common value to equate the
2219  * key column with.
2220  */
2221  if (!lower_val || !upper_val)
2222  break;
2223 
2224  /* Create the test expression */
2225  estate = CreateExecutorState();
2226  oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
2227  test_expr = make_partition_op_expr(key, i, BTEqualStrategyNumber,
2228  (Expr *) lower_val,
2229  (Expr *) upper_val);
2230  fix_opfuncids((Node *) test_expr);
2231  test_exprstate = ExecInitExpr(test_expr, NULL);
2232  test_result = ExecEvalExprSwitchContext(test_exprstate,
2233  GetPerTupleExprContext(estate),
2234  &isNull);
2235  MemoryContextSwitchTo(oldcxt);
2236  FreeExecutorState(estate);
2237 
2238  /* If not equal, go generate the OR expressions */
2239  if (!DatumGetBool(test_result))
2240  break;
2241 
2242  /*
2243  * The bounds for the last key column can't be equal, because such a
2244  * range partition would never be allowed to be defined (it would have
2245  * an empty range otherwise).
2246  */
2247  if (i == key->partnatts - 1)
2248  elog(ERROR, "invalid range bound specification");
2249 
2250  /* Equal, so generate keyCol = lower_val expression */
2251  result = lappend(result,
2253  keyCol, (Expr *) lower_val));
2254 
2255  i++;
2256  }
2257 
2258  /* First pair of lower_val and upper_val that are not equal. */
2259  lower_or_start_datum = cell1;
2260  upper_or_start_datum = cell2;
2261 
2262  /* OR will have as many arms as there are key columns left. */
2263  num_or_arms = key->partnatts - i;
2264  current_or_arm = 0;
2265  lower_or_arms = upper_or_arms = NIL;
2266  need_next_lower_arm = need_next_upper_arm = true;
2267  while (current_or_arm < num_or_arms)
2268  {
2269  List *lower_or_arm_args = NIL,
2270  *upper_or_arm_args = NIL;
2271 
2272  /* Restart scan of columns from the i'th one */
2273  j = i;
2274  partexprs_item = partexprs_item_saved;
2275 
2276  for_both_cell(cell1, lower_or_start_datum, cell2, upper_or_start_datum)
2277  {
2278  PartitionRangeDatum *ldatum_next = NULL,
2279  *udatum_next = NULL;
2280 
2281  ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
2282  if (lnext(cell1))
2283  ldatum_next = castNode(PartitionRangeDatum,
2284  lfirst(lnext(cell1)));
2285  udatum = castNode(PartitionRangeDatum, lfirst(cell2));
2286  if (lnext(cell2))
2287  udatum_next = castNode(PartitionRangeDatum,
2288  lfirst(lnext(cell2)));
2289  get_range_key_properties(key, j, ldatum, udatum,
2290  &partexprs_item,
2291  &keyCol,
2292  &lower_val, &upper_val);
2293 
2294  if (need_next_lower_arm && lower_val)
2295  {
2296  uint16 strategy;
2297 
2298  /*
2299  * For the non-last columns of this arm, use the EQ operator.
2300  * For the last column of this arm, use GT, unless this is the
2301  * last column of the whole bound check, or the next bound
2302  * datum is MINVALUE, in which case use GE.
2303  */
2304  if (j - i < current_or_arm)
2305  strategy = BTEqualStrategyNumber;
2306  else if (j == key->partnatts - 1 ||
2307  (ldatum_next &&
2308  ldatum_next->kind == PARTITION_RANGE_DATUM_MINVALUE))
2309  strategy = BTGreaterEqualStrategyNumber;
2310  else
2311  strategy = BTGreaterStrategyNumber;
2312 
2313  lower_or_arm_args = lappend(lower_or_arm_args,
2314  make_partition_op_expr(key, j,
2315  strategy,
2316  keyCol,
2317  (Expr *) lower_val));
2318  }
2319 
2320  if (need_next_upper_arm && upper_val)
2321  {
2322  uint16 strategy;
2323 
2324  /*
2325  * For the non-last columns of this arm, use the EQ operator.
2326  * For the last column of this arm, use LT, unless the next
2327  * bound datum is MAXVALUE, in which case use LE.
2328  */
2329  if (j - i < current_or_arm)
2330  strategy = BTEqualStrategyNumber;
2331  else if (udatum_next &&
2332  udatum_next->kind == PARTITION_RANGE_DATUM_MAXVALUE)
2333  strategy = BTLessEqualStrategyNumber;
2334  else
2335  strategy = BTLessStrategyNumber;
2336 
2337  upper_or_arm_args = lappend(upper_or_arm_args,
2338  make_partition_op_expr(key, j,
2339  strategy,
2340  keyCol,
2341  (Expr *) upper_val));
2342  }
2343 
2344  /*
2345  * Did we generate enough of OR's arguments? First arm considers
2346  * the first of the remaining columns, second arm considers first
2347  * two of the remaining columns, and so on.
2348  */
2349  ++j;
2350  if (j - i > current_or_arm)
2351  {
2352  /*
2353  * We must not emit any more arms if the new column that will
2354  * be considered is unbounded, or this one was.
2355  */
2356  if (!lower_val || !ldatum_next ||
2357  ldatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
2358  need_next_lower_arm = false;
2359  if (!upper_val || !udatum_next ||
2360  udatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
2361  need_next_upper_arm = false;
2362  break;
2363  }
2364  }
2365 
2366  if (lower_or_arm_args != NIL)
2367  lower_or_arms = lappend(lower_or_arms,
2368  list_length(lower_or_arm_args) > 1
2369  ? makeBoolExpr(AND_EXPR, lower_or_arm_args, -1)
2370  : linitial(lower_or_arm_args));
2371 
2372  if (upper_or_arm_args != NIL)
2373  upper_or_arms = lappend(upper_or_arms,
2374  list_length(upper_or_arm_args) > 1
2375  ? makeBoolExpr(AND_EXPR, upper_or_arm_args, -1)
2376  : linitial(upper_or_arm_args));
2377 
2378  /* If no work to do in the next iteration, break away. */
2379  if (!need_next_lower_arm && !need_next_upper_arm)
2380  break;
2381 
2382  ++current_or_arm;
2383  }
2384 
2385  /*
2386  * Generate the OR expressions for each of lower and upper bounds (if
2387  * required), and append to the list of implicitly ANDed list of
2388  * expressions.
2389  */
2390  if (lower_or_arms != NIL)
2391  result = lappend(result,
2392  list_length(lower_or_arms) > 1
2393  ? makeBoolExpr(OR_EXPR, lower_or_arms, -1)
2394  : linitial(lower_or_arms));
2395  if (upper_or_arms != NIL)
2396  result = lappend(result,
2397  list_length(upper_or_arms) > 1
2398  ? makeBoolExpr(OR_EXPR, upper_or_arms, -1)
2399  : linitial(upper_or_arms));
2400 
2401  /*
2402  * As noted above, for non-default, we return list with constant TRUE. If
2403  * the result is NIL during the recursive call for default, it implies
2404  * this is the only other partition which can hold every value of the key
2405  * except NULL. Hence we return the NullTest result skipped earlier.
2406  */
2407  if (result == NIL)
2408  result = for_default
2409  ? get_range_nulltest(key)
2411 
2412  return result;
2413 }
#define NIL
Definition: pg_list.h:69
void * stringToNode(char *str)
Definition: read.c:38
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
static Expr * make_partition_op_expr(PartitionKey key, int keynum, uint16 strategy, Expr *arg1, Expr *arg2)
Definition: partition.c:1595
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:291
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:180
#define castNode(_type_, nodeptr)
Definition: nodes.h:581
void fix_opfuncids(Node *node)
Definition: nodeFuncs.c:1582
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
PartitionRangeDatumKind kind
Definition: parsenodes.h:834
Definition: nodes.h:512
#define false
Definition: c.h:255
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:368
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
#define list_make1(x1)
Definition: pg_list.h:139
void FreeExecutorState(EState *estate)
Definition: execUtils.c:186
#define GetPerTupleExprContext(estate)
Definition: executor.h:467
#define true
Definition: c.h:251
unsigned short uint16
Definition: c.h:295
MemoryContext es_query_cxt
Definition: execnodes.h:472
#define linitial(l)
Definition: pg_list.h:111
#define ERROR
Definition: elog.h:43
Node * makeBoolConst(bool value, bool isnull)
Definition: makefuncs.c:356
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:1938
#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:2067
EState * CreateExecutorState(void)
Definition: execUtils.c:81
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:670
#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:593
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:1982
#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:641

◆ get_qual_from_partbound()

List* get_qual_from_partbound ( Relation  rel,
Relation  parent,
PartitionBoundSpec spec 
)

Definition at line 1413 of file partition.c.

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

Referenced by ATExecAttachPartition(), and generate_partition_qual().

1415 {
1416  PartitionKey key = RelationGetPartitionKey(parent);
1417  List *my_qual = NIL;
1418 
1419  Assert(key != NULL);
1420 
1421  switch (key->strategy)
1422  {
1425  my_qual = get_qual_for_hash(parent, spec);
1426  break;
1427 
1430  my_qual = get_qual_for_list(parent, spec);
1431  break;
1432 
1435  my_qual = get_qual_for_range(parent, spec, false);
1436  break;
1437 
1438  default:
1439  elog(ERROR, "unexpected partition strategy: %d",
1440  (int) key->strategy);
1441  }
1442 
1443  return my_qual;
1444 }
#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:1752
static List * get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default)
Definition: partition.c:2067
static List * get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
Definition: partition.c:1669
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:787
#define Assert(condition)
Definition: c.h:670
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:788
#define RelationGetPartitionKey(relation)
Definition: rel.h:593
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:789
#define elog
Definition: elog.h:219
Definition: pg_list.h:45

◆ get_range_key_properties()

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 1938 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().

1944 {
1945  /* Get partition key expression for this column */
1946  if (key->partattrs[keynum] != 0)
1947  {
1948  *keyCol = (Expr *) makeVar(1,
1949  key->partattrs[keynum],
1950  key->parttypid[keynum],
1951  key->parttypmod[keynum],
1952  key->parttypcoll[keynum],
1953  0);
1954  }
1955  else
1956  {
1957  if (*partexprs_item == NULL)
1958  elog(ERROR, "wrong number of partition key expressions");
1959  *keyCol = copyObject(lfirst(*partexprs_item));
1960  *partexprs_item = lnext(*partexprs_item);
1961  }
1962 
1963  /* Get appropriate Const nodes for the bounds */
1964  if (ldatum->kind == PARTITION_RANGE_DATUM_VALUE)
1965  *lower_val = castNode(Const, copyObject(ldatum->value));
1966  else
1967  *lower_val = NULL;
1968 
1969  if (udatum->kind == PARTITION_RANGE_DATUM_VALUE)
1970  *upper_val = castNode(Const, copyObject(udatum->value));
1971  else
1972  *upper_val = NULL;
1973 }
#define castNode(_type_, nodeptr)
Definition: nodes.h:581
PartitionRangeDatumKind kind
Definition: parsenodes.h:834
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:625

◆ get_range_nulltest()

static List * get_range_nulltest ( PartitionKey  key)
static

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

1983 {
1984  List *result = NIL;
1985  NullTest *nulltest;
1986  ListCell *partexprs_item;
1987  int i;
1988 
1989  partexprs_item = list_head(key->partexprs);
1990  for (i = 0; i < key->partnatts; i++)
1991  {
1992  Expr *keyCol;
1993 
1994  if (key->partattrs[i] != 0)
1995  {
1996  keyCol = (Expr *) makeVar(1,
1997  key->partattrs[i],
1998  key->parttypid[i],
1999  key->parttypmod[i],
2000  key->parttypcoll[i],
2001  0);
2002  }
2003  else
2004  {
2005  if (partexprs_item == NULL)
2006  elog(ERROR, "wrong number of partition key expressions");
2007  keyCol = copyObject(lfirst(partexprs_item));
2008  partexprs_item = lnext(partexprs_item);
2009  }
2010 
2011  nulltest = makeNode(NullTest);
2012  nulltest->arg = keyCol;
2013  nulltest->nulltesttype = IS_NOT_NULL;
2014  nulltest->argisrow = false;
2015  nulltest->location = -1;
2016  result = lappend(result, nulltest);
2017  }
2018 
2019  return result;
2020 }
#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:1187
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:1188
int32 * parttypmod
Definition: rel.h:70
#define makeNode(_type_)
Definition: nodes.h:560
#define lfirst(lc)
Definition: pg_list.h:106
int location
Definition: primnodes.h:1190
int i
bool argisrow
Definition: primnodes.h:1189
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:625
Definition: pg_list.h:45

◆ make_one_range_bound()

static PartitionRangeBound * make_one_range_bound ( PartitionKey  key,
int  index,
List datums,
bool  lower 
)
static

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

2658 {
2659  PartitionRangeBound *bound;
2660  ListCell *lc;
2661  int i;
2662 
2663  Assert(datums != NIL);
2664 
2665  bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
2666  bound->index = index;
2667  bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
2668  bound->kind = (PartitionRangeDatumKind *) palloc0(key->partnatts *
2669  sizeof(PartitionRangeDatumKind));
2670  bound->lower = lower;
2671 
2672  i = 0;
2673  foreach(lc, datums)
2674  {
2676 
2677  /* What's contained in this range datum? */
2678  bound->kind[i] = datum->kind;
2679 
2680  if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
2681  {
2682  Const *val = castNode(Const, datum->value);
2683 
2684  if (val->constisnull)
2685  elog(ERROR, "invalid range bound datum");
2686  bound->datums[i] = val->constvalue;
2687  }
2688 
2689  i++;
2690  }
2691 
2692  return bound;
2693 }
Datum constvalue
Definition: primnodes.h:196
#define NIL
Definition: pg_list.h:69
PartitionRangeDatumKind * kind
Definition: partition.c:137
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:43
#define castNode(_type_, nodeptr)
Definition: nodes.h:581
PartitionRangeDatumKind
Definition: parsenodes.h:823
PartitionRangeDatumKind kind
Definition: parsenodes.h:834
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:670
#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

◆ make_partition_op_expr()

static Expr * make_partition_op_expr ( PartitionKey  key,
int  keynum,
uint16  strategy,
Expr arg1,
Expr arg2 
)
static

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

1597 {
1598  Oid operoid;
1599  bool need_relabel = false;
1600  Expr *result = NULL;
1601 
1602  /* Get the correct btree operator for this partitioning column */
1603  operoid = get_partition_operator(key, keynum, strategy, &need_relabel);
1604 
1605  /*
1606  * Chosen operator may be such that the non-Const operand needs to be
1607  * coerced, so apply the same; see the comment in
1608  * get_partition_operator().
1609  */
1610  if (!IsA(arg1, Const) &&
1611  (need_relabel ||
1612  key->partcollation[keynum] != key->parttypcoll[keynum]))
1613  arg1 = (Expr *) makeRelabelType(arg1,
1614  key->partopcintype[keynum],
1615  -1,
1616  key->partcollation[keynum],
1618 
1619  /* Generate the actual expression */
1620  switch (key->strategy)
1621  {
1623  {
1624  ScalarArrayOpExpr *saopexpr;
1625 
1626  /* Build leftop = ANY (rightop) */
1627  saopexpr = makeNode(ScalarArrayOpExpr);
1628  saopexpr->opno = operoid;
1629  saopexpr->opfuncid = get_opcode(operoid);
1630  saopexpr->useOr = true;
1631  saopexpr->inputcollid = key->partcollation[keynum];
1632  saopexpr->args = list_make2(arg1, arg2);
1633  saopexpr->location = -1;
1634 
1635  result = (Expr *) saopexpr;
1636  break;
1637  }
1638 
1640  result = make_opclause(operoid,
1641  BOOLOID,
1642  false,
1643  arg1, arg2,
1644  InvalidOid,
1645  key->partcollation[keynum]);
1646  break;
1647 
1648  default:
1649  elog(ERROR, "invalid partitioning strategy");
1650  break;
1651  }
1652 
1653  return result;
1654 }
#define list_make2(x1, x2)
Definition: pg_list.h:140
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
static Oid get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel)
Definition: partition.c:1545
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:401
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:560
#define BOOLOID
Definition: pg_type.h:288
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:788
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:789
Oid * partopcintype
Definition: rel.h:62
#define elog
Definition: elog.h:219

◆ map_partition_varattnos()

List* map_partition_varattnos ( List expr,
int  target_varno,
Relation  partrel,
Relation  parent,
bool found_whole_row 
)

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

1464 {
1465  bool my_found_whole_row = false;
1466 
1467  if (expr != NIL)
1468  {
1469  AttrNumber *part_attnos;
1470 
1471  part_attnos = convert_tuples_by_name_map(RelationGetDescr(partrel),
1472  RelationGetDescr(parent),
1473  gettext_noop("could not convert row type"));
1474  expr = (List *) map_variable_attnos((Node *) expr,
1475  target_varno, 0,
1476  part_attnos,
1477  RelationGetDescr(parent)->natts,
1478  RelationGetForm(partrel)->reltype,
1479  &my_found_whole_row);
1480  }
1481 
1482  if (found_whole_row)
1483  *found_whole_row = my_found_whole_row;
1484 
1485  return expr;
1486 }
#define NIL
Definition: pg_list.h:69
#define RelationGetDescr(relation)
Definition: rel.h:437
#define RelationGetForm(relation)
Definition: rel.h:419
#define gettext_noop(x)
Definition: c.h:981
Definition: nodes.h:512
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

◆ partition_bound_bsearch()

static int partition_bound_bsearch ( PartitionKey  key,
PartitionBoundInfo  boundinfo,
void *  probe,
bool  probe_is_bound,
bool is_equal 
)
static

Definition at line 2880 of file partition.c.

References PartitionBoundInfoData::ndatums, and partition_bound_cmp().

Referenced by check_new_partition_bound(), and get_partition_for_tuple().

2882 {
2883  int lo,
2884  hi,
2885  mid;
2886 
2887  lo = -1;
2888  hi = boundinfo->ndatums - 1;
2889  while (lo < hi)
2890  {
2891  int32 cmpval;
2892 
2893  mid = (lo + hi + 1) / 2;
2894  cmpval = partition_bound_cmp(key, boundinfo, mid, probe,
2895  probe_is_bound);
2896  if (cmpval <= 0)
2897  {
2898  lo = mid;
2899  *is_equal = (cmpval == 0);
2900 
2901  if (*is_equal)
2902  break;
2903  }
2904  else
2905  hi = mid - 1;
2906  }
2907 
2908  return lo;
2909 }
signed int int32
Definition: c.h:284
static int32 partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo, int offset, void *probe, bool probe_is_bound)
Definition: partition.c:2810

◆ partition_bound_cmp()

static int32 partition_bound_cmp ( PartitionKey  key,
PartitionBoundInfo  boundinfo,
int  offset,
void *  probe,
bool  probe_is_bound 
)
static

Definition at line 2810 of file partition.c.

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

Referenced by check_new_partition_bound(), and partition_bound_bsearch().

2812 {
2813  Datum *bound_datums = boundinfo->datums[offset];
2814  int32 cmpval = -1;
2815 
2816  switch (key->strategy)
2817  {
2819  {
2820  PartitionBoundSpec *spec = (PartitionBoundSpec *) probe;
2821 
2822  cmpval = partition_hbound_cmp(DatumGetInt32(bound_datums[0]),
2823  DatumGetInt32(bound_datums[1]),
2824  spec->modulus, spec->remainder);
2825  break;
2826  }
2828  cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
2829  key->partcollation[0],
2830  bound_datums[0],
2831  *(Datum *) probe));
2832  break;
2833 
2835  {
2836  PartitionRangeDatumKind *kind = boundinfo->kind[offset];
2837 
2838  if (probe_is_bound)
2839  {
2840  /*
2841  * We need to pass whether the existing bound is a lower
2842  * bound, so that two equal-valued lower and upper bounds
2843  * are not regarded equal.
2844  */
2845  bool lower = boundinfo->indexes[offset] < 0;
2846 
2847  cmpval = partition_rbound_cmp(key,
2848  bound_datums, kind, lower,
2849  (PartitionRangeBound *) probe);
2850  }
2851  else
2852  cmpval = partition_rbound_datum_cmp(key,
2853  bound_datums, kind,
2854  (Datum *) probe);
2855  break;
2856  }
2857 
2858  default:
2859  elog(ERROR, "unexpected partition strategy: %d",
2860  (int) key->strategy);
2861  }
2862 
2863  return cmpval;
2864 }
PartitionRangeDatumKind ** kind
Definition: partition.c:99
#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:823
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:284
#define ERROR
Definition: elog.h:43
static int32 partition_rbound_cmp(PartitionKey key, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partition.c:2720
static int32 partition_rbound_datum_cmp(PartitionKey key, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *tuple_datums)
Definition: partition.c:2778
static int32 partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
Definition: partition.c:2621
Oid * partcollation
Definition: rel.h:66
uintptr_t Datum
Definition: postgres.h:372
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:787
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:788
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:789
#define elog
Definition: elog.h:219

◆ partition_bounds_copy()

PartitionBoundInfo partition_bounds_copy ( PartitionBoundInfo  src,
PartitionKey  key 
)

Definition at line 855 of file partition.c.

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

Referenced by set_relation_partition_info().

857 {
859  int i;
860  int ndatums;
861  int partnatts;
862  int num_indexes;
863 
865 
866  dest->strategy = src->strategy;
867  ndatums = dest->ndatums = src->ndatums;
868  partnatts = key->partnatts;
869 
870  num_indexes = get_partition_bound_num_indexes(src);
871 
872  /* List partitioned tables have only a single partition key. */
873  Assert(key->strategy != PARTITION_STRATEGY_LIST || partnatts == 1);
874 
875  dest->datums = (Datum **) palloc(sizeof(Datum *) * ndatums);
876 
877  if (src->kind != NULL)
878  {
879  dest->kind = (PartitionRangeDatumKind **) palloc(ndatums *
880  sizeof(PartitionRangeDatumKind *));
881  for (i = 0; i < ndatums; i++)
882  {
883  dest->kind[i] = (PartitionRangeDatumKind *) palloc(partnatts *
884  sizeof(PartitionRangeDatumKind));
885 
886  memcpy(dest->kind[i], src->kind[i],
887  sizeof(PartitionRangeDatumKind) * key->partnatts);
888  }
889  }
890  else
891  dest->kind = NULL;
892 
893  for (i = 0; i < ndatums; i++)
894  {
895  int j;
896 
897  /*
898  * For a corresponding to hash partition, datums array will have two
899  * elements - modulus and remainder.
900  */
901  bool hash_part = (key->strategy == PARTITION_STRATEGY_HASH);
902  int natts = hash_part ? 2 : partnatts;
903 
904  dest->datums[i] = (Datum *) palloc(sizeof(Datum) * natts);
905 
906  for (j = 0; j < natts; j++)
907  {
908  bool byval;
909  int typlen;
910 
911  if (hash_part)
912  {
913  typlen = sizeof(int32); /* Always int4 */
914  byval = true; /* int4 is pass-by-value */
915  }
916  else
917  {
918  byval = key->parttypbyval[j];
919  typlen = key->parttyplen[j];
920  }
921 
922  if (dest->kind == NULL ||
923  dest->kind[i][j] == PARTITION_RANGE_DATUM_VALUE)
924  dest->datums[i][j] = datumCopy(src->datums[i][j],
925  byval, typlen);
926  }
927  }
928 
929  dest->indexes = (int *) palloc(sizeof(int) * num_indexes);
930  memcpy(dest->indexes, src->indexes, sizeof(int) * num_indexes);
931 
932  dest->null_index = src->null_index;
933  dest->default_index = src->default_index;
934 
935  return dest;
936 }
PartitionRangeDatumKind ** kind
Definition: partition.c:99
PartitionRangeDatumKind
Definition: parsenodes.h:823
static int get_partition_bound_num_indexes(PartitionBoundInfo b)
Definition: partition.c:3018
char strategy
Definition: rel.h:54
signed int int32
Definition: c.h:284
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
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:787
bool * parttypbyval
Definition: rel.h:72
struct PartitionBoundInfoData * PartitionBoundInfo
Definition: partition.h:31
#define Assert(condition)
Definition: c.h:670
int16 * parttyplen
Definition: rel.h:71
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:788
void * palloc(Size size)
Definition: mcxt.c:848
int i

◆ partition_bounds_equal()

bool partition_bounds_equal ( int  partnatts,
int16 parttyplen,
bool parttypbyval,
PartitionBoundInfo  b1,
PartitionBoundInfo  b2 
)

Definition at line 742 of file partition.c.

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

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

744 {
745  int i;
746 
747  if (b1->strategy != b2->strategy)
748  return false;
749 
750  if (b1->ndatums != b2->ndatums)
751  return false;
752 
753  if (b1->null_index != b2->null_index)
754  return false;
755 
756  if (b1->default_index != b2->default_index)
757  return false;
758 
760  {
761  int greatest_modulus = get_greatest_modulus(b1);
762 
763  /*
764  * If two hash partitioned tables have different greatest moduli,
765  * their partition schemes don't match.
766  */
767  if (greatest_modulus != get_greatest_modulus(b2))
768  return false;
769 
770  /*
771  * We arrange the partitions in the ascending order of their modulus
772  * and remainders. Also every modulus is factor of next larger
773  * modulus. Therefore we can safely store index of a given partition
774  * in indexes array at remainder of that partition. Also entries at
775  * (remainder + N * modulus) positions in indexes array are all same
776  * for (modulus, remainder) specification for any partition. Thus
777  * datums array from both the given bounds are same, if and only if
778  * their indexes array will be same. So, it suffices to compare
779  * indexes array.
780  */
781  for (i = 0; i < greatest_modulus; i++)
782  if (b1->indexes[i] != b2->indexes[i])
783  return false;
784 
785 #ifdef USE_ASSERT_CHECKING
786 
787  /*
788  * Nonetheless make sure that the bounds are indeed same when the
789  * indexes match. Hash partition bound stores modulus and remainder
790  * at b1->datums[i][0] and b1->datums[i][1] position respectively.
791  */
792  for (i = 0; i < b1->ndatums; i++)
793  Assert((b1->datums[i][0] == b2->datums[i][0] &&
794  b1->datums[i][1] == b2->datums[i][1]));
795 #endif
796  }
797  else
798  {
799  for (i = 0; i < b1->ndatums; i++)
800  {
801  int j;
802 
803  for (j = 0; j < partnatts; j++)
804  {
805  /* For range partitions, the bounds might not be finite. */
806  if (b1->kind != NULL)
807  {
808  /* The different kinds of bound all differ from each other */
809  if (b1->kind[i][j] != b2->kind[i][j])
810  return false;
811 
812  /*
813  * Non-finite bounds are equal without further
814  * examination.
815  */
816  if (b1->kind[i][j] != PARTITION_RANGE_DATUM_VALUE)
817  continue;
818  }
819 
820  /*
821  * Compare the actual values. Note that it would be both
822  * incorrect and unsafe to invoke the comparison operator
823  * derived from the partitioning specification here. It would
824  * be incorrect because we want the relcache entry to be
825  * updated for ANY change to the partition bounds, not just
826  * those that the partitioning operator thinks are
827  * significant. It would be unsafe because we might reach
828  * this code in the context of an aborted transaction, and an
829  * arbitrary partitioning operator might not be safe in that
830  * context. datumIsEqual() should be simple enough to be
831  * safe.
832  */
833  if (!datumIsEqual(b1->datums[i][j], b2->datums[i][j],
834  parttypbyval[j], parttyplen[j]))
835  return false;
836  }
837 
838  if (b1->indexes[i] != b2->indexes[i])
839  return false;
840  }
841 
842  /* There are ndatums+1 indexes in case of range partitions */
843  if (b1->strategy == PARTITION_STRATEGY_RANGE &&
844  b1->indexes[i] != b2->indexes[i])
845  return false;
846  }
847  return true;
848 }
PartitionRangeDatumKind ** kind
Definition: partition.c:99
static int get_greatest_modulus(PartitionBoundInfo b)
Definition: partition.c:3060
bool datumIsEqual(Datum value1, Datum value2, bool typByVal, int typLen)
Definition: datum.c:219
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:787
#define Assert(condition)
Definition: c.h:670
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:789
int i

◆ partition_hbound_cmp()

static int32 partition_hbound_cmp ( int  modulus1,
int  remainder1,
int  modulus2,
int  remainder2 
)
static

Definition at line 2621 of file partition.c.

Referenced by partition_bound_cmp(), and qsort_partition_hbound_cmp().

2622 {
2623  if (modulus1 < modulus2)
2624  return -1;
2625  if (modulus1 > modulus2)
2626  return 1;
2627  if (modulus1 == modulus2 && remainder1 != remainder2)
2628  return (remainder1 > remainder2) ? 1 : -1;
2629  return 0;
2630 }

◆ partition_rbound_cmp()

static int32 partition_rbound_cmp ( PartitionKey  key,
Datum datums1,
PartitionRangeDatumKind kind1,
bool  lower1,
PartitionRangeBound b2 
)
static

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

2723 {
2724  int32 cmpval = 0; /* placate compiler */
2725  int i;
2726  Datum *datums2 = b2->datums;
2727  PartitionRangeDatumKind *kind2 = b2->kind;
2728  bool lower2 = b2->lower;
2729 
2730  for (i = 0; i < key->partnatts; i++)
2731  {
2732  /*
2733  * First, handle cases where the column is unbounded, which should not
2734  * invoke the comparison procedure, and should not consider any later
2735  * columns. Note that the PartitionRangeDatumKind enum elements
2736  * compare the same way as the values they represent.
2737  */
2738  if (kind1[i] < kind2[i])
2739  return -1;
2740  else if (kind1[i] > kind2[i])
2741  return 1;
2742  else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
2743 
2744  /*
2745  * The column bounds are both MINVALUE or both MAXVALUE. No later
2746  * columns should be considered, but we still need to compare
2747  * whether they are upper or lower bounds.
2748  */
2749  break;
2750 
2751  cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
2752  key->partcollation[i],
2753  datums1[i],
2754  datums2[i]));
2755  if (cmpval != 0)
2756  break;
2757  }
2758 
2759  /*
2760  * If the comparison is anything other than equal, we're done. If they
2761  * compare equal though, we still have to consider whether the boundaries
2762  * are inclusive or exclusive. Exclusive one is considered smaller of the
2763  * two.
2764  */
2765  if (cmpval == 0 && lower1 != lower2)
2766  cmpval = lower1 ? 1 : -1;
2767 
2768  return cmpval;
2769 }
PartitionRangeDatumKind * kind
Definition: partition.c:137
#define DatumGetInt32(X)
Definition: postgres.h:478
FmgrInfo * partsupfunc
Definition: rel.h:63
PartitionRangeDatumKind
Definition: parsenodes.h:823
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1042
signed int int32
Definition: c.h:284
Oid * partcollation
Definition: rel.h:66
uintptr_t Datum
Definition: postgres.h:372
int16 partnatts
Definition: rel.h:55
int i

◆ partition_rbound_datum_cmp()

static int32 partition_rbound_datum_cmp ( PartitionKey  key,
Datum rb_datums,
PartitionRangeDatumKind rb_kind,
Datum tuple_datums 
)
static

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

2781 {
2782  int i;
2783  int32 cmpval = -1;
2784 
2785  for (i = 0; i < key->partnatts; i++)
2786  {
2787  if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
2788  return -1;
2789  else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
2790  return 1;
2791 
2792  cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
2793  key->partcollation[i],
2794  rb_datums[i],
2795  tuple_datums[i]));
2796  if (cmpval != 0)
2797  break;
2798  }
2799 
2800  return cmpval;
2801 }
#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:284
Oid * partcollation
Definition: rel.h:66
int16 partnatts
Definition: rel.h:55
int i

◆ PG_FUNCTION_INFO_V1()

PG_FUNCTION_INFO_V1 ( satisfies_hash_partition  )

◆ qsort_partition_hbound_cmp()

static int32 qsort_partition_hbound_cmp ( const void *  a,
const void *  b 
)
static

Definition at line 2606 of file partition.c.

References PartitionHashBound::modulus, partition_hbound_cmp(), and PartitionHashBound::remainder.

Referenced by RelationBuildPartitionDesc().

2607 {
2608  PartitionHashBound *h1 = (*(PartitionHashBound *const *) a);
2609  PartitionHashBound *h2 = (*(PartitionHashBound *const *) b);
2610 
2611  return partition_hbound_cmp(h1->modulus, h1->remainder,
2612  h2->modulus, h2->remainder);
2613 }
static int32 partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
Definition: partition.c:2621

◆ qsort_partition_list_value_cmp()

static int32 qsort_partition_list_value_cmp ( const void *  a,
const void *  b,
void *  arg 
)
static

Definition at line 2638 of file partition.c.

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

Referenced by RelationBuildPartitionDesc().

2639 {
2640  Datum val1 = (*(const PartitionListValue **) a)->value,
2641  val2 = (*(const PartitionListValue **) b)->value;
2642  PartitionKey key = (PartitionKey) arg;
2643 
2645  key->partcollation[0],
2646  val1, val2));
2647 }
#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

◆ qsort_partition_rbound_cmp()

static int32 qsort_partition_rbound_cmp ( const void *  a,
const void *  b,
void *  arg 
)
static

Definition at line 2697 of file partition.c.

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

Referenced by RelationBuildPartitionDesc().

2698 {
2699  PartitionRangeBound *b1 = (*(PartitionRangeBound *const *) a);
2700  PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
2701  PartitionKey key = (PartitionKey) arg;
2702 
2703  return partition_rbound_cmp(key, b1->datums, b1->kind, b1->lower, b2);
2704 }
PartitionRangeDatumKind * kind
Definition: partition.c:137
static int32 partition_rbound_cmp(PartitionKey key, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partition.c:2720
struct PartitionKeyData * PartitionKey
Definition: rel.h:77
void * arg

◆ RelationBuildPartitionDesc()

void RelationBuildPartitionDesc ( Relation  rel)

Definition at line 196 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, PartitionHashBound::index, PartitionListValue::index, PartitionRangeBound::index, PartitionBoundInfoData::indexes, Int32GetDatum, 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(), PartitionHashBound::modulus, PartitionBoundSpec::modulus, PartitionBoundInfoData::ndatums, NIL, NoLock, PartitionDescData::nparts, PartitionBoundInfoData::null_index, PartitionDescData::oids, palloc(), palloc0(), PARTITION_RANGE_DATUM_VALUE, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionKeyData::partnatts, PartitionKeyData::parttypbyval, PartitionKeyData::parttyplen, pfree(), qsort, qsort_arg(), qsort_partition_hbound_cmp(), qsort_partition_list_value_cmp(), qsort_partition_rbound_cmp(), RelationData::rd_partdesc, RelationData::rd_pdcxt, RelationGetPartitionKey, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RELOID, PartitionHashBound::remainder, PartitionBoundSpec::remainder, SearchSysCache1(), PartitionKeyData::strategy, PartitionBoundInfoData::strategy, PartitionBoundSpec::strategy, stringToNode(), SysCacheGetAttr(), TextDatumGetCString, upper(), PartitionBoundSpec::upperdatums, val, PartitionListValue::value, and value.

Referenced by RelationBuildDesc(), and RelationCacheInitializePhase3().

197 {
198  List *inhoids,
199  *partoids;
200  Oid *oids = NULL;
201  List *boundspecs = NIL;
202  ListCell *cell;
203  int i,
204  nparts;
206  PartitionDesc result;
207  MemoryContext oldcxt;
208 
209  int ndatums = 0;
210  int default_index = -1;
211 
212  /* Hash partitioning specific */
213  PartitionHashBound **hbounds = NULL;
214 
215  /* List partitioning specific */
216  PartitionListValue **all_values = NULL;
217  int null_index = -1;
218 
219  /* Range partitioning specific */
220  PartitionRangeBound **rbounds = NULL;
221 
222  /*
223  * The following could happen in situations where rel has a pg_class entry
224  * but not the pg_partitioned_table entry yet.
225  */
226  if (key == NULL)
227  return;
228 
229  /* Get partition oids from pg_inherits */
231 
232  /* Collect bound spec nodes in a list */
233  i = 0;
234  partoids = NIL;
235  foreach(cell, inhoids)
236  {
237  Oid inhrelid = lfirst_oid(cell);
238  HeapTuple tuple;
239  Datum datum;
240  bool isnull;
241  Node *boundspec;
242 
243  tuple = SearchSysCache1(RELOID, inhrelid);
244  if (!HeapTupleIsValid(tuple))
245  elog(ERROR, "cache lookup failed for relation %u", inhrelid);
246 
247  /*
248  * It is possible that the pg_class tuple of a partition has not been
249  * updated yet to set its relpartbound field. The only case where
250  * this happens is when we open the parent relation to check using its
251  * partition descriptor that a new partition's bound does not overlap
252  * some existing partition.
253  */
254  if (!((Form_pg_class) GETSTRUCT(tuple))->relispartition)
255  {
256  ReleaseSysCache(tuple);
257  continue;
258  }
259 
260  datum = SysCacheGetAttr(RELOID, tuple,
262  &isnull);
263  Assert(!isnull);
264  boundspec = (Node *) stringToNode(TextDatumGetCString(datum));
265 
266  /*
267  * Sanity check: If the PartitionBoundSpec says this is the default
268  * partition, its OID should correspond to whatever's stored in
269  * pg_partitioned_table.partdefid; if not, the catalog is corrupt.
270  */
271  if (castNode(PartitionBoundSpec, boundspec)->is_default)
272  {
273  Oid partdefid;
274 
276  if (partdefid != inhrelid)
277  elog(ERROR, "expected partdefid %u, but got %u",
278  inhrelid, partdefid);
279  }
280 
281  boundspecs = lappend(boundspecs, boundspec);
282  partoids = lappend_oid(partoids, inhrelid);
283  ReleaseSysCache(tuple);
284  }
285 
286  nparts = list_length(partoids);
287 
288  if (nparts > 0)
289  {
290  oids = (Oid *) palloc(nparts * sizeof(Oid));
291  i = 0;
292  foreach(cell, partoids)
293  oids[i++] = lfirst_oid(cell);
294 
295  /* Convert from node to the internal representation */
296  if (key->strategy == PARTITION_STRATEGY_HASH)
297  {
298  ndatums = nparts;
299  hbounds = (PartitionHashBound **)
300  palloc(nparts * sizeof(PartitionHashBound *));
301 
302  i = 0;
303  foreach(cell, boundspecs)
304  {
306  lfirst(cell));
307 
308  if (spec->strategy != PARTITION_STRATEGY_HASH)
309  elog(ERROR, "invalid strategy in partition bound spec");
310 
311  hbounds[i] = (PartitionHashBound *)
312  palloc(sizeof(PartitionHashBound));
313 
314  hbounds[i]->modulus = spec->modulus;
315  hbounds[i]->remainder = spec->remainder;
316  hbounds[i]->index = i;
317  i++;
318  }
319 
320  /* Sort all the bounds in ascending order */
321  qsort(hbounds, nparts, sizeof(PartitionHashBound *),
323  }
324  else if (key->strategy == PARTITION_STRATEGY_LIST)
325  {
326  List *non_null_values = NIL;
327 
328  /*
329  * Create a unified list of non-null values across all partitions.
330  */
331  i = 0;
332  null_index = -1;
333  foreach(cell, boundspecs)
334  {
336  lfirst(cell));
337  ListCell *c;
338 
339  if (spec->strategy != PARTITION_STRATEGY_LIST)
340  elog(ERROR, "invalid strategy in partition bound spec");
341 
342  /*
343  * Note the index of the partition bound spec for the default
344  * partition. There's no datum to add to the list of non-null
345  * datums for this partition.
346  */
347  if (spec->is_default)
348  {
349  default_index = i;
350  i++;
351  continue;
352  }
353 
354  foreach(c, spec->listdatums)
355  {
356  Const *val = castNode(Const, lfirst(c));
357  PartitionListValue *list_value = NULL;
358 
359  if (!val->constisnull)
360  {
361  list_value = (PartitionListValue *)
362  palloc0(sizeof(PartitionListValue));
363  list_value->index = i;
364  list_value->value = val->constvalue;
365  }
366  else
367  {
368  /*
369  * Never put a null into the values array, flag
370  * instead for the code further down below where we
371  * construct the actual relcache struct.
372  */
373  if (null_index != -1)
374  elog(ERROR, "found null more than once");
375  null_index = i;
376  }
377 
378  if (list_value)
379  non_null_values = lappend(non_null_values,
380  list_value);
381  }
382 
383  i++;
384  }
385 
386  ndatums = list_length(non_null_values);
387 
388  /*
389  * Collect all list values in one array. Alongside the value, we
390  * also save the index of partition the value comes from.
391  */
392  all_values = (PartitionListValue **) palloc(ndatums *
393  sizeof(PartitionListValue *));
394  i = 0;
395  foreach(cell, non_null_values)
396  {
397  PartitionListValue *src = lfirst(cell);
398 
399  all_values[i] = (PartitionListValue *)
400  palloc(sizeof(PartitionListValue));
401  all_values[i]->value = src->value;
402  all_values[i]->index = src->index;
403  i++;
404  }
405 
406  qsort_arg(all_values, ndatums, sizeof(PartitionListValue *),
407  qsort_partition_list_value_cmp, (void *) key);
408  }
409  else if (key->strategy == PARTITION_STRATEGY_RANGE)
410  {
411  int k;
412  PartitionRangeBound **all_bounds,
413  *prev;
414 
415  all_bounds = (PartitionRangeBound **) palloc0(2 * nparts *
416  sizeof(PartitionRangeBound *));
417 
418  /*
419  * Create a unified list of range bounds across all the
420  * partitions.
421  */
422  i = ndatums = 0;
423  foreach(cell, boundspecs)
424  {
426  lfirst(cell));
428  *upper;
429 
430  if (spec->strategy != PARTITION_STRATEGY_RANGE)
431  elog(ERROR, "invalid strategy in partition bound spec");
432 
433  /*
434  * Note the index of the partition bound spec for the default
435  * partition. There's no datum to add to the allbounds array
436  * for this partition.
437  */
438  if (spec->is_default)
439  {
440  default_index = i++;
441  continue;
442  }
443 
444  lower = make_one_range_bound(key, i, spec->lowerdatums,
445  true);
446  upper = make_one_range_bound(key, i, spec->upperdatums,
447  false);
448  all_bounds[ndatums++] = lower;
449  all_bounds[ndatums++] = upper;
450  i++;
451  }
452 
453  Assert(ndatums == nparts * 2 ||
454  (default_index != -1 && ndatums == (nparts - 1) * 2));
455 
456  /* Sort all the bounds in ascending order */
457  qsort_arg(all_bounds, ndatums,
458  sizeof(PartitionRangeBound *),
460  (void *) key);
461 
462  /* Save distinct bounds from all_bounds into rbounds. */
463  rbounds = (PartitionRangeBound **)
464  palloc(ndatums * sizeof(PartitionRangeBound *));
465  k = 0;
466  prev = NULL;
467  for (i = 0; i < ndatums; i++)
468  {
469  PartitionRangeBound *cur = all_bounds[i];
470  bool is_distinct = false;
471  int j;
472 
473  /* Is the current bound distinct from the previous one? */
474  for (j = 0; j < key->partnatts; j++)
475  {
476  Datum cmpval;
477 
478  if (prev == NULL || cur->kind[j] != prev->kind[j])
479  {
480  is_distinct = true;
481  break;
482  }
483 
484  /*
485  * If the bounds are both MINVALUE or MAXVALUE, stop now
486  * and treat them as equal, since any values after this
487  * point must be ignored.
488  */
489  if (cur->kind[j] != PARTITION_RANGE_DATUM_VALUE)
490  break;
491 
492  cmpval = FunctionCall2Coll(&key->partsupfunc[j],
493  key->partcollation[j],
494  cur->datums[j],
495  prev->datums[j]);
496  if (DatumGetInt32(cmpval) != 0)
497  {
498  is_distinct = true;
499  break;
500  }
501  }
502 
503  /*
504  * Only if the bound is distinct save it into a temporary
505  * array i.e. rbounds which is later copied into boundinfo
506  * datums array.
507  */
508  if (is_distinct)
509  rbounds[k++] = all_bounds[i];
510 
511  prev = cur;
512  }
513 
514  /* Update ndatums to hold the count of distinct datums. */
515  ndatums = k;
516  }
517  else
518  elog(ERROR, "unexpected partition strategy: %d",
519  (int) key->strategy);
520  }
521 
522  /* Now build the actual relcache partition descriptor */
526  oldcxt = MemoryContextSwitchTo(rel->rd_pdcxt);
527 
528  result = (PartitionDescData *) palloc0(sizeof(PartitionDescData));
529  result->nparts = nparts;
530  if (nparts > 0)
531  {
532  PartitionBoundInfo boundinfo;
533  int *mapping;
534  int next_index = 0;
535 
536  result->oids = (Oid *) palloc0(nparts * sizeof(Oid));
537 
538  boundinfo = (PartitionBoundInfoData *)
540  boundinfo->strategy = key->strategy;
541  boundinfo->default_index = -1;
542  boundinfo->ndatums = ndatums;
543  boundinfo->null_index = -1;
544  boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *));
545 
546  /* Initialize mapping array with invalid values */
547  mapping = (int *) palloc(sizeof(int) * nparts);
548  for (i = 0; i < nparts; i++)
549  mapping[i] = -1;
550 
551  switch (key->strategy)
552  {
554  {
555  /* Modulus are stored in ascending order */
556  int greatest_modulus = hbounds[ndatums - 1]->modulus;
557 
558  boundinfo->indexes = (int *) palloc(greatest_modulus *
559  sizeof(int));
560 
561  for (i = 0; i < greatest_modulus; i++)
562  boundinfo->indexes[i] = -1;
563 
564  for (i = 0; i < nparts; i++)
565  {
566  int modulus = hbounds[i]->modulus;
567  int remainder = hbounds[i]->remainder;
568 
569  boundinfo->datums[i] = (Datum *) palloc(2 *
570  sizeof(Datum));
571  boundinfo->datums[i][0] = Int32GetDatum(modulus);
572  boundinfo->datums[i][1] = Int32GetDatum(remainder);
573 
574  while (remainder < greatest_modulus)
575  {
576  /* overlap? */
577  Assert(boundinfo->indexes[remainder] == -1);
578  boundinfo->indexes[remainder] = i;
579  remainder += modulus;
580  }
581 
582  mapping[hbounds[i]->index] = i;
583  pfree(hbounds[i]);
584  }
585  pfree(hbounds);
586  break;
587  }
588 
590  {
591  boundinfo->indexes = (int *) palloc(ndatums * sizeof(int));
592 
593  /*
594  * Copy values. Indexes of individual values are mapped
595  * to canonical values so that they match for any two list
596  * partitioned tables with same number of partitions and
597  * same lists per partition. One way to canonicalize is
598  * to assign the index in all_values[] of the smallest
599  * value of each partition, as the index of all of the
600  * partition's values.
601  */
602  for (i = 0; i < ndatums; i++)
603  {
604  boundinfo->datums[i] = (Datum *) palloc(sizeof(Datum));
605  boundinfo->datums[i][0] = datumCopy(all_values[i]->value,
606  key->parttypbyval[0],
607  key->parttyplen[0]);
608 
609  /* If the old index has no mapping, assign one */
610  if (mapping[all_values[i]->index] == -1)
611  mapping[all_values[i]->index] = next_index++;
612 
613  boundinfo->indexes[i] = mapping[all_values[i]->index];
614  }
615 
616  /*
617  * If null-accepting partition has no mapped index yet,
618  * assign one. This could happen if such partition
619  * accepts only null and hence not covered in the above
620  * loop which only handled non-null values.
621  */
622  if (null_index != -1)
623  {
624  Assert(null_index >= 0);
625  if (mapping[null_index] == -1)
626  mapping[null_index] = next_index++;
627  boundinfo->null_index = mapping[null_index];
628  }
629 
630  /* Assign mapped index for the default partition. */
631  if (default_index != -1)
632  {
633  /*
634  * The default partition accepts any value not
635  * specified in the lists of other partitions, hence
636  * it should not get mapped index while assigning
637  * those for non-null datums.
638  */
639  Assert(default_index >= 0 &&
640  mapping[default_index] == -1);
641  mapping[default_index] = next_index++;
642  boundinfo->default_index = mapping[default_index];
643  }
644 
645  /* All partition must now have a valid mapping */
646  Assert(next_index == nparts);
647  break;
648  }
649 
651  {
652  boundinfo->kind = (PartitionRangeDatumKind **)
653  palloc(ndatums *
654  sizeof(PartitionRangeDatumKind *));
655  boundinfo->indexes = (int *) palloc((ndatums + 1) *
656  sizeof(int));
657 
658  for (i = 0; i < ndatums; i++)
659  {
660  int j;
661 
662  boundinfo->datums[i] = (Datum *) palloc(key->partnatts *
663  sizeof(Datum));
664  boundinfo->kind[i] = (PartitionRangeDatumKind *)
665  palloc(key->partnatts *
666  sizeof(PartitionRangeDatumKind));
667  for (j = 0; j < key->partnatts; j++)
668  {
669  if (rbounds[i]->kind[j] == PARTITION_RANGE_DATUM_VALUE)
670  boundinfo->datums[i][j] =
671  datumCopy(rbounds[i]->datums[j],
672  key->parttypbyval[j],
673  key->parttyplen[j]);
674  boundinfo->kind[i][j] = rbounds[i]->kind[j];
675  }
676 
677  /*
678  * There is no mapping for invalid indexes.
679  *
680  * Any lower bounds in the rbounds array have invalid
681  * indexes assigned, because the values between the
682  * previous bound (if there is one) and this (lower)
683  * bound are not part of the range of any existing
684  * partition.
685  */
686  if (rbounds[i]->lower)
687  boundinfo->indexes[i] = -1;
688  else
689  {
690  int orig_index = rbounds[i]->index;
691 
692  /* If the old index has no mapping, assign one */
693  if (mapping[orig_index] == -1)
694  mapping[orig_index] = next_index++;
695 
696  boundinfo->indexes[i] = mapping[orig_index];
697  }
698  }
699 
700  /* Assign mapped index for the default partition. */
701  if (default_index != -1)
702  {
703  Assert(default_index >= 0 && mapping[default_index] == -1);
704  mapping[default_index] = next_index++;
705  boundinfo->default_index = mapping[default_index];
706  }
707  boundinfo->indexes[i] = -1;
708  break;
709  }
710 
711  default:
712  elog(ERROR, "unexpected partition strategy: %d",
713  (int) key->strategy);
714  }
715 
716  result->boundinfo = boundinfo;
717 
718  /*
719  * Now assign OIDs from the original array into mapped indexes of the
720  * result array. Order of OIDs in the former is defined by the
721  * catalog scan that retrieved them, whereas that in the latter is
722  * defined by canonicalized representation of the partition bounds.
723  */
724  for (i = 0; i < nparts; i++)
725  result->oids[mapping[i]] = oids[i];
726  pfree(mapping);
727  }
728 
729  MemoryContextSwitchTo(oldcxt);
730  rel->rd_partdesc = result;
731 }
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:99
PartitionRangeDatumKind * kind
Definition: partition.c:137
#define GETSTRUCT(TUP)
Definition: htup_details.h:661
static int32 qsort_partition_hbound_cmp(const void *a, const void *b)
Definition: partition.c:2606
#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:581
PartitionRangeDatumKind
Definition: parsenodes.h:823
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:2697
Definition: nodes.h:512
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:40
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:170
static int32 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
Definition: partition.c:2638
char * c
#define NoLock
Definition: lockdefs.h:34
#define RelationGetRelationName(relation)
Definition: rel.h:445
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:128
Oid get_default_partition_oid(Oid parentId)
Definition: partition.c:2935
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:2657
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:342
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
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:787
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:670
#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:788
#define RelationGetPartitionKey(relation)
Definition: rel.h:593
FormData_pg_class * Form_pg_class
Definition: pg_class.h:95
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:789
#define Int32GetDatum(X)
Definition: postgres.h:485
void * palloc(Size size)
Definition: mcxt.c:848
int i
#define elog
Definition: elog.h:219
#define qsort(a, b, c, d)
Definition: port.h:408
Definition: pg_list.h:45
#define RelationGetRelid(relation)
Definition: rel.h:425
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

◆ RelationGetPartitionQual()

List* RelationGetPartitionQual ( Relation  rel)

Definition at line 1494 of file partition.c.

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

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

1495 {
1496  /* Quick exit */
1497  if (!rel->rd_rel->relispartition)
1498  return NIL;
1499 
1500  return generate_partition_qual(rel);
1501 }
#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:2428

◆ satisfies_hash_partition()

Datum satisfies_hash_partition ( PG_FUNCTION_ARGS  )

Definition at line 3120 of file partition.c.

References AccessShareLock, ARR_ELEMTYPE, Assert, DatumGetUInt64, deconstruct_array(), ereport, errcode(), errmsg(), ERROR, fmgr_info_copy(), FmgrInfo::fn_mcxt, format_type_be(), FunctionCall2, get_fn_expr_argtype(), get_fn_expr_variadic(), get_rel_name(), get_typlenbyvalalign(), hash(), hash_combine64(), HASH_PARTITION_SEED, i, IsBinaryCoercible(), MemoryContextAllocZero(), NoLock, offsetof, OidIsValid, PARTITION_MAX_KEYS, PARTITION_STRATEGY_HASH, PartitionKeyData::partnatts, PartitionKeyData::partsupfunc, PartitionKeyData::parttypid, PG_ARGISNULL, PG_GETARG_ARRAYTYPE_P, PG_GETARG_DATUM, PG_GETARG_INT32, PG_GETARG_OID, PG_NARGS, PG_RETURN_BOOL, PG_RETURN_NULL, RelationData::rd_rel, relation_close(), RelationGetPartitionKey, RELKIND_PARTITIONED_TABLE, PartitionKeyData::strategy, try_relation_open(), and UInt64GetDatum.

3121 {
3122  typedef struct ColumnsHashData
3123  {
3124  Oid relid;
3125  int nkeys;
3126  Oid variadic_type;
3127  int16 variadic_typlen;
3128  bool variadic_typbyval;
3129  char variadic_typalign;
3130  FmgrInfo partsupfunc[PARTITION_MAX_KEYS];
3131  } ColumnsHashData;
3132  Oid parentId;
3133  int modulus;
3134  int remainder;
3136  ColumnsHashData *my_extra;
3137  uint64 rowHash = 0;
3138 
3139  /* Return null if the parent OID, modulus, or remainder is NULL. */
3140  if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2))
3141  PG_RETURN_NULL();
3142  parentId = PG_GETARG_OID(0);
3143  modulus = PG_GETARG_INT32(1);
3144  remainder = PG_GETARG_INT32(2);
3145 
3146  /* Sanity check modulus and remainder. */
3147  if (modulus <= 0)
3148  ereport(ERROR,
3149  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3150  errmsg("modulus for hash partition must be a positive integer")));
3151  if (remainder < 0)
3152  ereport(ERROR,
3153  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3154  errmsg("remainder for hash partition must be a non-negative integer")));
3155  if (remainder >= modulus)
3156  ereport(ERROR,
3157  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3158  errmsg("remainder for hash partition must be less than modulus")));
3159 
3160  /*
3161  * Cache hash function information.
3162  */
3163  my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
3164  if (my_extra == NULL || my_extra->relid != parentId)
3165  {
3166  Relation parent;
3167  PartitionKey key;
3168  int j;
3169 
3170  /* Open parent relation and fetch partition keyinfo */
3171  parent = try_relation_open(parentId, AccessShareLock);
3172  if (parent == NULL)
3173  PG_RETURN_NULL();
3174  key = RelationGetPartitionKey(parent);
3175 
3176  /* Reject parent table that is not hash-partitioned. */
3177  if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE ||
3179  ereport(ERROR,
3180  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3181  errmsg("\"%s\" is not a hash partitioned table",
3182  get_rel_name(parentId))));
3183 
3184  if (!get_fn_expr_variadic(fcinfo->flinfo))
3185  {
3186  int nargs = PG_NARGS() - 3;
3187 
3188  /* complain if wrong number of column values */
3189  if (key->partnatts != nargs)
3190  ereport(ERROR,
3191  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3192  errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
3193  key->partnatts, nargs)));
3194 
3195  /* allocate space for our cache */
3196  fcinfo->flinfo->fn_extra =
3197  MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
3198  offsetof(ColumnsHashData, partsupfunc) +
3199  sizeof(FmgrInfo) * nargs);
3200  my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
3201  my_extra->relid = parentId;
3202  my_extra->nkeys = key->partnatts;
3203 
3204  /* check argument types and save fmgr_infos */
3205  for (j = 0; j < key->partnatts; ++j)
3206  {
3207  Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, j + 3);
3208 
3209  if (argtype != key->parttypid[j] && !IsBinaryCoercible(argtype, key->parttypid[j]))
3210  ereport(ERROR,
3211  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3212  errmsg("column %d of the partition key has type \"%s\", but supplied value is of type \"%s\"",
3213  j + 1, format_type_be(key->parttypid[j]), format_type_be(argtype))));
3214 
3215  fmgr_info_copy(&my_extra->partsupfunc[j],
3216  &key->partsupfunc[j],
3217  fcinfo->flinfo->fn_mcxt);
3218  }
3219 
3220  }
3221  else
3222  {
3223  ArrayType *variadic_array = PG_GETARG_ARRAYTYPE_P(3);
3224 
3225  /* allocate space for our cache -- just one FmgrInfo in this case */
3226  fcinfo->flinfo->fn_extra =
3227  MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
3228  offsetof(ColumnsHashData, partsupfunc) +
3229  sizeof(FmgrInfo));
3230  my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
3231  my_extra->relid = parentId;
3232  my_extra->nkeys = key->partnatts;
3233  my_extra->variadic_type = ARR_ELEMTYPE(variadic_array);
3234  get_typlenbyvalalign(my_extra->variadic_type,
3235  &my_extra->variadic_typlen,
3236  &my_extra->variadic_typbyval,
3237  &my_extra->variadic_typalign);
3238 
3239  /* check argument types */
3240  for (j = 0; j < key->partnatts; ++j)
3241  if (key->parttypid[j] != my_extra->variadic_type)
3242  ereport(ERROR,
3243  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3244  errmsg("column %d of the partition key has type \"%s\", but supplied value is of type \"%s\"",
3245  j + 1,
3246  format_type_be(key->parttypid[j]),
3247  format_type_be(my_extra->variadic_type))));
3248 
3249  fmgr_info_copy(&my_extra->partsupfunc[0],
3250  &key->partsupfunc[0],
3251  fcinfo->flinfo->fn_mcxt);
3252  }
3253 
3254  /* Hold lock until commit */
3255  relation_close(parent, NoLock);
3256  }
3257 
3258  if (!OidIsValid(my_extra->variadic_type))
3259  {
3260  int nkeys = my_extra->nkeys;
3261  int i;
3262 
3263  /*
3264  * For a non-variadic call, neither the number of arguments nor their
3265  * types can change across calls, so avoid the expense of rechecking
3266  * here.
3267  */
3268 
3269  for (i = 0; i < nkeys; i++)
3270  {
3271  Datum hash;
3272 
3273  /* keys start from fourth argument of function. */
3274  int argno = i + 3;
3275 
3276  if (PG_ARGISNULL(argno))
3277  continue;
3278 
3279  Assert(OidIsValid(my_extra->partsupfunc[i].fn_oid));
3280 
3281  hash = FunctionCall2(&my_extra->partsupfunc[i],
3282  PG_GETARG_DATUM(argno),
3283  seed);
3284 
3285  /* Form a single 64-bit hash value */
3286  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
3287  }
3288  }
3289  else
3290  {
3291  ArrayType *variadic_array = PG_GETARG_ARRAYTYPE_P(3);
3292  int i;
3293  int nelems;
3294  Datum *datum;
3295  bool *isnull;
3296 
3297  deconstruct_array(variadic_array,
3298  my_extra->variadic_type,
3299  my_extra->variadic_typlen,
3300  my_extra->variadic_typbyval,
3301  my_extra->variadic_typalign,
3302  &datum, &isnull, &nelems);
3303 
3304  /* complain if wrong number of column values */
3305  if (nelems != my_extra->nkeys)
3306  ereport(ERROR,
3307  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3308  errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
3309  my_extra->nkeys, nelems)));
3310 
3311  for (i = 0; i < nelems; i++)
3312  {
3313  Datum hash;
3314 
3315  if (isnull[i])
3316  continue;
3317 
3318  Assert(OidIsValid(my_extra->partsupfunc[0].fn_oid));
3319 
3320  hash = FunctionCall2(&my_extra->partsupfunc[0],
3321  datum[i],
3322  seed);
3323 
3324  /* Form a single 64-bit hash value */
3325  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
3326  }
3327  }
3328 
3329  PG_RETURN_BOOL(rowHash % modulus == remainder);
3330 }
signed short int16
Definition: c.h:283
#define PG_GETARG_INT32(n)
Definition: fmgr.h:234
Definition: fmgr.h:56
MemoryContext fn_mcxt
Definition: fmgr.h:65
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1153
FmgrInfo * partsupfunc
Definition: rel.h:63
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition: lsyscache.c:2040
#define PG_GETARG_DATUM(n)
Definition: fmgr.h:233
#define UInt64GetDatum(X)
Definition: postgres.h:654
#define FunctionCall2(flinfo, arg1, arg2)
Definition: fmgr.h:605
#define AccessShareLock
Definition: lockdefs.h:36
bool get_fn_expr_variadic(FmgrInfo *flinfo)
Definition: fmgr.c:2038
int errcode(int sqlerrcode)
Definition: elog.c:575
#define PARTITION_MAX_KEYS
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: heapam.c:1266
char * format_type_be(Oid type_oid)
Definition: format_type.c:94
char strategy
Definition: rel.h:54
Form_pg_class rd_rel
Definition: rel.h:114
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:576
#define PG_GETARG_ARRAYTYPE_P(n)
Definition: array.h:248
#define ERROR
Definition: elog.h:43
Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)
Definition: fmgr.c:1904
void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt)
Definition: fmgr.c:519
#define NoLock
Definition: lockdefs.h:34
#define PG_GETARG_OID(n)
Definition: fmgr.h:240
#define ereport(elevel, rest)
Definition: elog.h:122
Oid * parttypid
Definition: rel.h:69
bool IsBinaryCoercible(Oid srctype, Oid targettype)
static uint64 hash_combine64(uint64 a, uint64 b)
Definition: hashutils.h:29
#define RELKIND_PARTITIONED_TABLE
Definition: pg_class.h:168
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
uintptr_t Datum
Definition: postgres.h:372
int16 partnatts
Definition: rel.h:55
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:787
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:741
#define DatumGetUInt64(X)
Definition: postgres.h:640
#define PG_ARGISNULL(n)
Definition: fmgr.h:174
#define Assert(condition)
Definition: c.h:670
#define PG_NARGS()
Definition: fmgr.h:168
#define HASH_PARTITION_SEED
Definition: partition.h:23
#define RelationGetPartitionKey(relation)
Definition: rel.h:593
void deconstruct_array(ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3449
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
static unsigned hash(unsigned *uv, int n)
Definition: rege_dfa.c:541
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1745
#define ARR_ELEMTYPE(a)
Definition: array.h:277
#define PG_RETURN_NULL()
Definition: fmgr.h:305
#define offsetof(type, field)
Definition: c.h:593

◆ update_default_partition_oid()

void update_default_partition_oid ( Oid  parentId,
Oid  defaultPartId 
)

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

2961 {
2962  HeapTuple tuple;
2963  Relation pg_partitioned_table;
2964  Form_pg_partitioned_table part_table_form;
2965 
2966  pg_partitioned_table = heap_open(PartitionedRelationId, RowExclusiveLock);
2967 
2968  tuple = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(parentId));
2969 
2970  if (!HeapTupleIsValid(tuple))
2971  elog(ERROR, "cache lookup failed for partition key of relation %u",
2972  parentId);
2973 
2974  part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
2975  part_table_form->partdefid = defaultPartId;
2976  CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple);
2977 
2978  heap_freetuple(tuple);
2979  heap_close(pg_partitioned_table, RowExclusiveLock);
2980 }
#define GETSTRUCT(TUP)
Definition: htup_details.h:661
#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