PostgreSQL Source Code  git master
partbounds.c File Reference
#include "postgres.h"
#include "access/relation.h"
#include "access/table.h"
#include "access/tableam.h"
#include "catalog/partition.h"
#include "catalog/pg_inherits.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 "parser/parse_coerce.h"
#include "partitioning/partbounds.h"
#include "partitioning/partdesc.h"
#include "partitioning/partprune.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/hashutils.h"
#include "utils/lsyscache.h"
#include "utils/partcache.h"
#include "utils/rel.h"
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
Include dependency graph for partbounds.c:

Go to the source code of this file.

Data Structures

struct  PartitionHashBound
 
struct  PartitionListValue
 
struct  PartitionRangeBound
 

Typedefs

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 PartitionBoundInfo create_hash_bounds (PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
 
static PartitionBoundInfo create_list_bounds (PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
 
static PartitionBoundInfo create_range_bounds (PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
 
static PartitionRangeBoundmake_one_partition_rbound (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 (int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
 
static int partition_range_bsearch (int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, PartitionRangeBound *probe, bool *is_equal)
 
static int get_partition_bound_num_indexes (PartitionBoundInfo b)
 
static Exprmake_partition_op_expr (PartitionKey key, int keynum, uint16 strategy, Expr *arg1, Expr *arg2)
 
static Oid get_partition_operator (PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel)
 
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 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_range_nulltest (PartitionKey key)
 
Listget_qual_from_partbound (Relation rel, Relation parent, PartitionBoundSpec *spec)
 
PartitionBoundInfo partition_bounds_create (PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
 
bool partition_bounds_equal (int partnatts, int16 *parttyplen, bool *parttypbyval, PartitionBoundInfo b1, PartitionBoundInfo b2)
 
PartitionBoundInfo partition_bounds_copy (PartitionBoundInfo src, PartitionKey key)
 
bool partitions_are_ordered (PartitionBoundInfo boundinfo, int nparts)
 
void check_new_partition_bound (char *relname, Relation parent, PartitionBoundSpec *spec)
 
void check_default_partition_contents (Relation parent, Relation default_rel, PartitionBoundSpec *new_spec)
 
int get_hash_partition_greatest_modulus (PartitionBoundInfo bound)
 
int32 partition_rbound_datum_cmp (FmgrInfo *partsupfunc, Oid *partcollation, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *tuple_datums, int n_tuple_datums)
 
int partition_list_bsearch (FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
 
int partition_range_datum_bsearch (FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, int nvalues, Datum *values, bool *is_equal)
 
int partition_hash_bsearch (PartitionBoundInfo boundinfo, int modulus, int remainder)
 
uint64 compute_partition_hash_value (int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *values, bool *isnull)
 
Datum satisfies_hash_partition (PG_FUNCTION_ARGS)
 

Typedef Documentation

◆ PartitionHashBound

◆ PartitionListValue

◆ PartitionRangeBound

Function Documentation

◆ check_default_partition_contents()

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

Definition at line 1226 of file partbounds.c.

References AccessExclusiveLock, CHECK_FOR_INTERRUPTS, CreateExecutorState(), DEBUG1, ExprContext::ecxt_scantuple, ereport, errcode(), errmsg(), ERROR, EState::es_tupleTable, ExecCheck(), ExecDropSingleTupleTableSlot(), ExecPrepareExpr(), find_all_inheritors(), ForwardScanDirection, FreeExecutorState(), get_proposed_default_constraint(), get_qual_for_list(), get_qual_for_range(), GetLatestSnapshot(), GetPerTupleExprContext, GetPerTupleMemoryContext, lfirst_oid, list_make1_oid, make_ands_explicit(), map_partition_varattnos(), MemoryContextSwitchTo(), NoLock, PartConstraintImpliedByRelConstraint(), PARTITION_STRATEGY_LIST, RelationData::rd_rel, RegisterSnapshot(), RelationGetRelationName, RelationGetRelid, ResetExprContext, PartitionBoundSpec::strategy, table_beginscan(), table_close(), table_endscan(), table_open(), table_scan_getnextslot(), table_slot_create(), UnregisterSnapshot(), and WARNING.

Referenced by DefineRelation().

1228 {
1229  List *new_part_constraints;
1230  List *def_part_constraints;
1231  List *all_parts;
1232  ListCell *lc;
1233 
1234  new_part_constraints = (new_spec->strategy == PARTITION_STRATEGY_LIST)
1235  ? get_qual_for_list(parent, new_spec)
1236  : get_qual_for_range(parent, new_spec, false);
1237  def_part_constraints =
1238  get_proposed_default_constraint(new_part_constraints);
1239 
1240  /*
1241  * Map the Vars in the constraint expression from parent's attnos to
1242  * default_rel's.
1243  */
1244  def_part_constraints =
1245  map_partition_varattnos(def_part_constraints, 1, default_rel,
1246  parent, NULL);
1247 
1248  /*
1249  * If the existing constraints on the default partition imply that it will
1250  * not contain any row that would belong to the new partition, we can
1251  * avoid scanning the default partition.
1252  */
1253  if (PartConstraintImpliedByRelConstraint(default_rel, def_part_constraints))
1254  {
1255  ereport(DEBUG1,
1256  (errmsg("updated partition constraint for default partition \"%s\" is implied by existing constraints",
1257  RelationGetRelationName(default_rel))));
1258  return;
1259  }
1260 
1261  /*
1262  * Scan the default partition and its subpartitions, and check for rows
1263  * that do not satisfy the revised partition constraints.
1264  */
1265  if (default_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1266  all_parts = find_all_inheritors(RelationGetRelid(default_rel),
1267  AccessExclusiveLock, NULL);
1268  else
1269  all_parts = list_make1_oid(RelationGetRelid(default_rel));
1270 
1271  foreach(lc, all_parts)
1272  {
1273  Oid part_relid = lfirst_oid(lc);
1274  Relation part_rel;
1275  Expr *partition_constraint;
1276  EState *estate;
1277  ExprState *partqualstate = NULL;
1278  Snapshot snapshot;
1279  ExprContext *econtext;
1280  TableScanDesc scan;
1281  MemoryContext oldCxt;
1282  TupleTableSlot *tupslot;
1283 
1284  /* Lock already taken above. */
1285  if (part_relid != RelationGetRelid(default_rel))
1286  {
1287  part_rel = table_open(part_relid, NoLock);
1288 
1289  /*
1290  * Map the Vars in the constraint expression from default_rel's
1291  * the sub-partition's.
1292  */
1293  partition_constraint = make_ands_explicit(def_part_constraints);
1294  partition_constraint = (Expr *)
1295  map_partition_varattnos((List *) partition_constraint, 1,
1296  part_rel, default_rel, NULL);
1297 
1298  /*
1299  * If the partition constraints on default partition child imply
1300  * that it will not contain any row that would belong to the new
1301  * partition, we can avoid scanning the child table.
1302  */
1304  def_part_constraints))
1305  {
1306  ereport(DEBUG1,
1307  (errmsg("updated partition constraint for default partition \"%s\" is implied by existing constraints",
1308  RelationGetRelationName(part_rel))));
1309 
1310  table_close(part_rel, NoLock);
1311  continue;
1312  }
1313  }
1314  else
1315  {
1316  part_rel = default_rel;
1317  partition_constraint = make_ands_explicit(def_part_constraints);
1318  }
1319 
1320  /*
1321  * Only RELKIND_RELATION relations (i.e. leaf partitions) need to be
1322  * scanned.
1323  */
1324  if (part_rel->rd_rel->relkind != RELKIND_RELATION)
1325  {
1326  if (part_rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1327  ereport(WARNING,
1328  (errcode(ERRCODE_CHECK_VIOLATION),
1329  errmsg("skipped scanning foreign table \"%s\" which is a partition of default partition \"%s\"",
1330  RelationGetRelationName(part_rel),
1331  RelationGetRelationName(default_rel))));
1332 
1333  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
1334  table_close(part_rel, NoLock);
1335 
1336  continue;
1337  }
1338 
1339  estate = CreateExecutorState();
1340 
1341  /* Build expression execution states for partition check quals */
1342  partqualstate = ExecPrepareExpr(partition_constraint, estate);
1343 
1344  econtext = GetPerTupleExprContext(estate);
1345  snapshot = RegisterSnapshot(GetLatestSnapshot());
1346  tupslot = table_slot_create(part_rel, &estate->es_tupleTable);
1347  scan = table_beginscan(part_rel, snapshot, 0, NULL);
1348 
1349  /*
1350  * Switch to per-tuple memory context and reset it for each tuple
1351  * produced, so we don't leak memory.
1352  */
1354 
1355  while (table_scan_getnextslot(scan, ForwardScanDirection, tupslot))
1356  {
1357  econtext->ecxt_scantuple = tupslot;
1358 
1359  if (!ExecCheck(partqualstate, econtext))
1360  ereport(ERROR,
1361  (errcode(ERRCODE_CHECK_VIOLATION),
1362  errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
1363  RelationGetRelationName(default_rel))));
1364 
1365  ResetExprContext(econtext);
1367  }
1368 
1369  MemoryContextSwitchTo(oldCxt);
1370  table_endscan(scan);
1371  UnregisterSnapshot(snapshot);
1373  FreeExecutorState(estate);
1374 
1375  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
1376  table_close(part_rel, NoLock); /* keep the lock until commit */
1377  }
1378 }
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:77
#define DEBUG1
Definition: elog.h:25
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:865
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int errcode(int sqlerrcode)
Definition: elog.c:608
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:872
Form_pg_class rd_rel
Definition: rel.h:83
unsigned int Oid
Definition: postgres_ext.h:31
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:490
void FreeExecutorState(EState *estate)
Definition: execUtils.c:190
#define GetPerTupleExprContext(estate)
Definition: executor.h:501
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel, bool *found_whole_row)
Definition: partition.c:201
#define ERROR
Definition: elog.h:43
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, struct ScanKeyData *key)
Definition: tableam.h:736
#define NoLock
Definition: lockdefs.h:34
List * get_proposed_default_constraint(List *new_part_constraints)
Definition: partition.c:353
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1224
#define RelationGetRelationName(relation)
Definition: rel.h:456
#define ereport(elevel, rest)
Definition: elog.h:141
EState * CreateExecutorState(void)
Definition: execUtils.c:88
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:907
#define WARNING
Definition: elog.h:40
List * es_tupleTable
Definition: execnodes.h:551
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:705
#define list_make1_oid(x1)
Definition: pg_list.h:249
static List * get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default)
Definition: partbounds.c:2283
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:223
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:799
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:381
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:506
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:831
#define AccessExclusiveLock
Definition: lockdefs.h:45
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:165
int errmsg(const char *fmt,...)
Definition: elog.c:822
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition: execExpr.c:597
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
Definition: pg_list.h:50
#define RelationGetRelid(relation)
Definition: rel.h:422
static List * get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:2074
#define ResetExprContext(econtext)
Definition: executor.h:495
#define lfirst_oid(lc)
Definition: pg_list.h:192
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:15318

◆ check_new_partition_bound()

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

Definition at line 934 of file partbounds.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_hash_partition_greatest_modulus(), get_range_partbound_string(), get_rel_name(), PartitionBoundInfoData::indexes, PartitionBoundSpec::is_default, sort-test::key, PartitionBoundInfoData::kind, PartitionRangeBound::kind, lfirst, PartitionBoundSpec::listdatums, PartitionBoundSpec::location, lower(), PartitionBoundSpec::lowerdatums, make_one_partition_rbound(), make_parsestate(), PartitionBoundSpec::modulus, PartitionBoundInfoData::ndatums, PartitionDescData::nparts, PartitionBoundInfoData::null_index, PartitionDescData::oids, parser_errposition(), PartitionKeyData::partcollation, partition_bound_accepts_nulls, partition_bound_has_default, partition_hash_bsearch(), partition_list_bsearch(), partition_range_bsearch(), partition_rbound_cmp(), PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionKeyData::partnatts, PartitionKeyData::partsupfunc, RelationGetPartitionDesc, RelationGetPartitionKey, PartitionHashBound::remainder, PartitionBoundSpec::remainder, PartitionKeyData::strategy, PartitionBoundInfoData::strategy, PartitionBoundSpec::strategy, upper(), PartitionBoundSpec::upperdatums, and val.

Referenced by ATExecAttachPartition(), and DefineRelation().

936 {
938  PartitionDesc partdesc = RelationGetPartitionDesc(parent);
939  PartitionBoundInfo boundinfo = partdesc->boundinfo;
940  ParseState *pstate = make_parsestate(NULL);
941  int with = -1;
942  bool overlap = false;
943 
944  if (spec->is_default)
945  {
946  /*
947  * The default partition bound never conflicts with any other
948  * partition's; if that's what we're attaching, the only possible
949  * problem is that one already exists, so check for that and we're
950  * done.
951  */
952  if (boundinfo == NULL || !partition_bound_has_default(boundinfo))
953  return;
954 
955  /* Default partition already exists, error out. */
956  ereport(ERROR,
957  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
958  errmsg("partition \"%s\" conflicts with existing default partition \"%s\"",
959  relname, get_rel_name(partdesc->oids[boundinfo->default_index])),
960  parser_errposition(pstate, spec->location)));
961  }
962 
963  switch (key->strategy)
964  {
966  {
968  Assert(spec->remainder >= 0 && spec->remainder < spec->modulus);
969 
970  if (partdesc->nparts > 0)
971  {
972  Datum **datums = boundinfo->datums;
973  int ndatums = boundinfo->ndatums;
974  int greatest_modulus;
975  int remainder;
976  int offset;
977  bool valid_modulus = true;
978  int prev_modulus, /* Previous largest modulus */
979  next_modulus; /* Next largest modulus */
980 
981  /*
982  * Check rule that every modulus must be a factor of the
983  * next larger modulus. For example, if you have a bunch
984  * of partitions that all have modulus 5, you can add a
985  * new partition with modulus 10 or a new partition with
986  * modulus 15, but you cannot add both a partition with
987  * modulus 10 and a partition with modulus 15, because 10
988  * is not a factor of 15.
989  *
990  * Get the greatest (modulus, remainder) pair contained in
991  * boundinfo->datums that is less than or equal to the
992  * (spec->modulus, spec->remainder) pair.
993  */
994  offset = partition_hash_bsearch(boundinfo,
995  spec->modulus,
996  spec->remainder);
997  if (offset < 0)
998  {
999  next_modulus = DatumGetInt32(datums[0][0]);
1000  valid_modulus = (next_modulus % spec->modulus) == 0;
1001  }
1002  else
1003  {
1004  prev_modulus = DatumGetInt32(datums[offset][0]);
1005  valid_modulus = (spec->modulus % prev_modulus) == 0;
1006 
1007  if (valid_modulus && (offset + 1) < ndatums)
1008  {
1009  next_modulus = DatumGetInt32(datums[offset + 1][0]);
1010  valid_modulus = (next_modulus % spec->modulus) == 0;
1011  }
1012  }
1013 
1014  if (!valid_modulus)
1015  ereport(ERROR,
1016  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1017  errmsg("every hash partition modulus must be a factor of the next larger modulus")));
1018 
1019  greatest_modulus = get_hash_partition_greatest_modulus(boundinfo);
1020  remainder = spec->remainder;
1021 
1022  /*
1023  * Normally, the lowest remainder that could conflict with
1024  * the new partition is equal to the remainder specified
1025  * for the new partition, but when the new partition has a
1026  * modulus higher than any used so far, we need to adjust.
1027  */
1028  if (remainder >= greatest_modulus)
1029  remainder = remainder % greatest_modulus;
1030 
1031  /* Check every potentially-conflicting remainder. */
1032  do
1033  {
1034  if (boundinfo->indexes[remainder] != -1)
1035  {
1036  overlap = true;
1037  with = boundinfo->indexes[remainder];
1038  break;
1039  }
1040  remainder += spec->modulus;
1041  } while (remainder < greatest_modulus);
1042  }
1043 
1044  break;
1045  }
1046 
1048  {
1050 
1051  if (partdesc->nparts > 0)
1052  {
1053  ListCell *cell;
1054 
1055  Assert(boundinfo &&
1056  boundinfo->strategy == PARTITION_STRATEGY_LIST &&
1057  (boundinfo->ndatums > 0 ||
1058  partition_bound_accepts_nulls(boundinfo) ||
1059  partition_bound_has_default(boundinfo)));
1060 
1061  foreach(cell, spec->listdatums)
1062  {
1063  Const *val = castNode(Const, lfirst(cell));
1064 
1065  if (!val->constisnull)
1066  {
1067  int offset;
1068  bool equal;
1069 
1070  offset = partition_list_bsearch(&key->partsupfunc[0],
1071  key->partcollation,
1072  boundinfo,
1073  val->constvalue,
1074  &equal);
1075  if (offset >= 0 && equal)
1076  {
1077  overlap = true;
1078  with = boundinfo->indexes[offset];
1079  break;
1080  }
1081  }
1082  else if (partition_bound_accepts_nulls(boundinfo))
1083  {
1084  overlap = true;
1085  with = boundinfo->null_index;
1086  break;
1087  }
1088  }
1089  }
1090 
1091  break;
1092  }
1093 
1095  {
1097  *upper;
1098 
1100  lower = make_one_partition_rbound(key, -1, spec->lowerdatums, true);
1101  upper = make_one_partition_rbound(key, -1, spec->upperdatums, false);
1102 
1103  /*
1104  * First check if the resulting range would be empty with
1105  * specified lower and upper bounds
1106  */
1108  key->partcollation, lower->datums,
1109  lower->kind, true, upper) >= 0)
1110  {
1111  ereport(ERROR,
1112  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1113  errmsg("empty range bound specified for partition \"%s\"",
1114  relname),
1115  errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
1118  parser_errposition(pstate, spec->location)));
1119  }
1120 
1121  if (partdesc->nparts > 0)
1122  {
1123  int offset;
1124  bool equal;
1125 
1126  Assert(boundinfo &&
1127  boundinfo->strategy == PARTITION_STRATEGY_RANGE &&
1128  (boundinfo->ndatums > 0 ||
1129  partition_bound_has_default(boundinfo)));
1130 
1131  /*
1132  * Test whether the new lower bound (which is treated
1133  * inclusively as part of the new partition) lies inside
1134  * an existing partition, or in a gap.
1135  *
1136  * If it's inside an existing partition, the bound at
1137  * offset + 1 will be the upper bound of that partition,
1138  * and its index will be >= 0.
1139  *
1140  * If it's in a gap, the bound at offset + 1 will be the
1141  * lower bound of the next partition, and its index will
1142  * be -1. This is also true if there is no next partition,
1143  * since the index array is initialised with an extra -1
1144  * at the end.
1145  */
1146  offset = partition_range_bsearch(key->partnatts,
1147  key->partsupfunc,
1148  key->partcollation,
1149  boundinfo, lower,
1150  &equal);
1151 
1152  if (boundinfo->indexes[offset + 1] < 0)
1153  {
1154  /*
1155  * Check that the new partition will fit in the gap.
1156  * For it to fit, the new upper bound must be less
1157  * than or equal to the lower bound of the next
1158  * partition, if there is one.
1159  */
1160  if (offset + 1 < boundinfo->ndatums)
1161  {
1162  int32 cmpval;
1163  Datum *datums;
1165  bool is_lower;
1166 
1167  datums = boundinfo->datums[offset + 1];
1168  kind = boundinfo->kind[offset + 1];
1169  is_lower = (boundinfo->indexes[offset + 1] == -1);
1170 
1171  cmpval = partition_rbound_cmp(key->partnatts,
1172  key->partsupfunc,
1173  key->partcollation,
1174  datums, kind,
1175  is_lower, upper);
1176  if (cmpval < 0)
1177  {
1178  /*
1179  * The new partition overlaps with the
1180  * existing partition between offset + 1 and
1181  * offset + 2.
1182  */
1183  overlap = true;
1184  with = boundinfo->indexes[offset + 2];
1185  }
1186  }
1187  }
1188  else
1189  {
1190  /*
1191  * The new partition overlaps with the existing
1192  * partition between offset and offset + 1.
1193  */
1194  overlap = true;
1195  with = boundinfo->indexes[offset + 1];
1196  }
1197  }
1198 
1199  break;
1200  }
1201 
1202  default:
1203  elog(ERROR, "unexpected partition strategy: %d",
1204  (int) key->strategy);
1205  }
1206 
1207  if (overlap)
1208  {
1209  Assert(with >= 0);
1210  ereport(ERROR,
1211  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1212  errmsg("partition \"%s\" would overlap partition \"%s\"",
1213  relname, get_rel_name(partdesc->oids[with])),
1214  parser_errposition(pstate, spec->location)));
1215  }
1216 }
Datum constvalue
Definition: primnodes.h:200
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
static int partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, PartitionRangeBound *probe, bool *is_equal)
Definition: partbounds.c:1619
PartitionRangeDatumKind * kind
Definition: partbounds.c:68
#define DatumGetInt32(X)
Definition: postgres.h:472
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3011
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:43
FmgrInfo * partsupfunc
Definition: partcache.h:35
#define castNode(_type_, nodeptr)
Definition: nodes.h:594
PartitionRangeDatumKind
Definition: parsenodes.h:834
int errcode(int sqlerrcode)
Definition: elog.c:608
Datum upper(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:74
NameData relname
Definition: pg_class.h:35
signed int int32
Definition: c.h:347
PartitionBoundInfo boundinfo
Definition: partdesc.h:29
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:43
static PartitionRangeBound * make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
Definition: partbounds.c:1405
#define ERROR
Definition: elog.h:43
int get_hash_partition_greatest_modulus(PartitionBoundInfo bound)
Definition: partbounds.c:1388
int errdetail(const char *fmt,...)
Definition: elog.c:955
#define partition_bound_has_default(bi)
Definition: partbounds.h:75
int partition_hash_bsearch(PartitionBoundInfo boundinfo, int modulus, int remainder)
Definition: partbounds.c:1708
#define ereport(elevel, rest)
Definition: elog.h:141
char * get_range_partbound_string(List *bound_datums)
Definition: ruleutils.c:11281
Oid * partcollation
Definition: partcache.h:38
uintptr_t Datum
Definition: postgres.h:367
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:798
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:74
int partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
Definition: partbounds.c:1576
static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partbounds.c:1461
#define Assert(condition)
Definition: c.h:739
#define lfirst(lc)
Definition: pg_list.h:190
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:110
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:799
#define RelationGetPartitionKey(relation)
Definition: rel.h:603
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:800
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define elog(elevel,...)
Definition: elog.h:228
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1730
long val
Definition: informix.c:664
bool constisnull
Definition: primnodes.h:201
#define RelationGetPartitionDesc(relation)
Definition: rel.h:609

◆ compute_partition_hash_value()

uint64 compute_partition_hash_value ( int  partnatts,
FmgrInfo partsupfunc,
Oid partcollation,
Datum values,
bool isnull 
)

Definition at line 2739 of file partbounds.c.

References Assert, DatumGetUInt64, FunctionCall2Coll(), hash(), hash_combine64(), HASH_PARTITION_SEED, i, OidIsValid, and UInt64GetDatum.

Referenced by get_matching_hash_bounds(), and get_partition_for_tuple().

2741 {
2742  int i;
2743  uint64 rowHash = 0;
2745 
2746  for (i = 0; i < partnatts; i++)
2747  {
2748  /* Nulls are just ignored */
2749  if (!isnull[i])
2750  {
2751  Datum hash;
2752 
2753  Assert(OidIsValid(partsupfunc[i].fn_oid));
2754 
2755  /*
2756  * Compute hash for each datum value by calling respective
2757  * datatype-specific hash functions of each partition key
2758  * attribute.
2759  */
2760  hash = FunctionCall2Coll(&partsupfunc[i], partcollation[i],
2761  values[i], seed);
2762 
2763  /* Form a single 64-bit hash value */
2764  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
2765  }
2766  }
2767 
2768  return rowHash;
2769 }
#define UInt64GetDatum(X)
Definition: postgres.h:648
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1150
#define OidIsValid(objectId)
Definition: c.h:645
static uint64 hash_combine64(uint64 a, uint64 b)
Definition: hashutils.h:48
uintptr_t Datum
Definition: postgres.h:367
#define DatumGetUInt64(X)
Definition: postgres.h:634
#define Assert(condition)
Definition: c.h:739
#define HASH_PARTITION_SEED
Definition: partition.h:20
static Datum values[MAXATTR]
Definition: bootstrap.c:167
int i
static unsigned hash(unsigned *uv, int n)
Definition: rege_dfa.c:541

◆ create_hash_bounds()

static PartitionBoundInfo create_hash_bounds ( PartitionBoundSpec **  boundspecs,
int  nparts,
PartitionKey  key,
int **  mapping 
)
static

Definition at line 226 of file partbounds.c.

References Assert, PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, elog, ERROR, i, PartitionHashBound::index, PartitionBoundInfoData::indexes, Int32GetDatum, PartitionHashBound::modulus, PartitionBoundSpec::modulus, PartitionBoundInfoData::ndatums, PartitionBoundInfoData::null_index, palloc(), palloc0(), PARTITION_STRATEGY_HASH, pfree(), qsort, qsort_partition_hbound_cmp(), PartitionHashBound::remainder, PartitionBoundSpec::remainder, PartitionKeyData::strategy, PartitionBoundInfoData::strategy, and PartitionBoundSpec::strategy.

Referenced by partition_bounds_create().

228 {
229  PartitionBoundInfo boundinfo;
230  PartitionHashBound **hbounds = NULL;
231  int i;
232  int ndatums = 0;
233  int greatest_modulus;
234 
235  boundinfo = (PartitionBoundInfoData *)
237  boundinfo->strategy = key->strategy;
238  /* No special hash partitions. */
239  boundinfo->null_index = -1;
240  boundinfo->default_index = -1;
241 
242  ndatums = nparts;
243  hbounds = (PartitionHashBound **)
244  palloc(nparts * sizeof(PartitionHashBound *));
245 
246  /* Convert from node to the internal representation */
247  for (i = 0; i < nparts; i++)
248  {
249  PartitionBoundSpec *spec = boundspecs[i];
250 
251  if (spec->strategy != PARTITION_STRATEGY_HASH)
252  elog(ERROR, "invalid strategy in partition bound spec");
253 
254  hbounds[i] = (PartitionHashBound *) palloc(sizeof(PartitionHashBound));
255  hbounds[i]->modulus = spec->modulus;
256  hbounds[i]->remainder = spec->remainder;
257  hbounds[i]->index = i;
258  }
259 
260  /* Sort all the bounds in ascending order */
261  qsort(hbounds, nparts, sizeof(PartitionHashBound *),
263 
264  /* After sorting, moduli are now stored in ascending order. */
265  greatest_modulus = hbounds[ndatums - 1]->modulus;
266 
267  boundinfo->ndatums = ndatums;
268  boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *));
269  boundinfo->indexes = (int *) palloc(greatest_modulus * sizeof(int));
270  for (i = 0; i < greatest_modulus; i++)
271  boundinfo->indexes[i] = -1;
272 
273  /*
274  * For hash partitioning, there are as many datums (modulus and remainder
275  * pairs) as there are partitions. Indexes are simply values ranging from
276  * 0 to (nparts - 1).
277  */
278  for (i = 0; i < nparts; i++)
279  {
280  int modulus = hbounds[i]->modulus;
281  int remainder = hbounds[i]->remainder;
282 
283  boundinfo->datums[i] = (Datum *) palloc(2 * sizeof(Datum));
284  boundinfo->datums[i][0] = Int32GetDatum(modulus);
285  boundinfo->datums[i][1] = Int32GetDatum(remainder);
286 
287  while (remainder < greatest_modulus)
288  {
289  /* overlap? */
290  Assert(boundinfo->indexes[remainder] == -1);
291  boundinfo->indexes[remainder] = i;
292  remainder += modulus;
293  }
294 
295  (*mapping)[hbounds[i]->index] = i;
296  pfree(hbounds[i]);
297  }
298  pfree(hbounds);
299 
300  return boundinfo;
301 }
static int32 qsort_partition_hbound_cmp(const void *a, const void *b)
Definition: partbounds.c:1748
void pfree(void *pointer)
Definition: mcxt.c:1056
#define ERROR
Definition: elog.h:43
void * palloc0(Size size)
Definition: mcxt.c:980
uintptr_t Datum
Definition: postgres.h:367
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:798
#define Assert(condition)
Definition: c.h:739
#define Int32GetDatum(X)
Definition: postgres.h:479
void * palloc(Size size)
Definition: mcxt.c:949
#define elog(elevel,...)
Definition: elog.h:228
int i
#define qsort(a, b, c, d)
Definition: port.h:488

◆ create_list_bounds()

static PartitionBoundInfo create_list_bounds ( PartitionBoundSpec **  boundspecs,
int  nparts,
PartitionKey  key,
int **  mapping 
)
static

Definition at line 308 of file partbounds.c.

References Assert, castNode, Const::constisnull, Const::constvalue, datumCopy(), PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, elog, ERROR, i, PartitionListValue::index, PartitionBoundInfoData::indexes, PartitionBoundSpec::is_default, lappend(), lfirst, list_length(), PartitionBoundSpec::listdatums, PartitionBoundInfoData::ndatums, NIL, PartitionBoundInfoData::null_index, palloc(), palloc0(), PARTITION_STRATEGY_LIST, qsort_arg(), qsort_partition_list_value_cmp(), PartitionKeyData::strategy, PartitionBoundInfoData::strategy, PartitionBoundSpec::strategy, val, PartitionListValue::value, and value.

Referenced by partition_bounds_create().

310 {
311  PartitionBoundInfo boundinfo;
312  PartitionListValue **all_values = NULL;
313  ListCell *cell;
314  int i = 0;
315  int ndatums = 0;
316  int next_index = 0;
317  int default_index = -1;
318  int null_index = -1;
319  List *non_null_values = NIL;
320 
321  boundinfo = (PartitionBoundInfoData *)
323  boundinfo->strategy = key->strategy;
324  /* Will be set correctly below. */
325  boundinfo->null_index = -1;
326  boundinfo->default_index = -1;
327 
328  /* Create a unified list of non-null values across all partitions. */
329  for (i = 0; i < nparts; i++)
330  {
331  PartitionBoundSpec *spec = boundspecs[i];
332  ListCell *c;
333 
334  if (spec->strategy != PARTITION_STRATEGY_LIST)
335  elog(ERROR, "invalid strategy in partition bound spec");
336 
337  /*
338  * Note the index of the partition bound spec for the default
339  * partition. There's no datum to add to the list on non-null datums
340  * for this partition.
341  */
342  if (spec->is_default)
343  {
344  default_index = i;
345  continue;
346  }
347 
348  foreach(c, spec->listdatums)
349  {
350  Const *val = castNode(Const, lfirst(c));
351  PartitionListValue *list_value = NULL;
352 
353  if (!val->constisnull)
354  {
355  list_value = (PartitionListValue *)
356  palloc0(sizeof(PartitionListValue));
357  list_value->index = i;
358  list_value->value = val->constvalue;
359  }
360  else
361  {
362  /*
363  * Never put a null into the values array; save the index of
364  * the partition that stores nulls, instead.
365  */
366  if (null_index != -1)
367  elog(ERROR, "found null more than once");
368  null_index = i;
369  }
370 
371  if (list_value)
372  non_null_values = lappend(non_null_values, list_value);
373  }
374  }
375 
376  ndatums = list_length(non_null_values);
377 
378  /*
379  * Collect all list values in one array. Alongside the value, we also save
380  * the index of partition the value comes from.
381  */
382  all_values = (PartitionListValue **)
383  palloc(ndatums * sizeof(PartitionListValue *));
384  i = 0;
385  foreach(cell, non_null_values)
386  {
387  PartitionListValue *src = lfirst(cell);
388 
389  all_values[i] = (PartitionListValue *)
390  palloc(sizeof(PartitionListValue));
391  all_values[i]->value = src->value;
392  all_values[i]->index = src->index;
393  i++;
394  }
395 
396  qsort_arg(all_values, ndatums, sizeof(PartitionListValue *),
397  qsort_partition_list_value_cmp, (void *) key);
398 
399  boundinfo->ndatums = ndatums;
400  boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *));
401  boundinfo->indexes = (int *) palloc(ndatums * sizeof(int));
402 
403  /*
404  * Copy values. Canonical indexes are values ranging from 0 to (nparts -
405  * 1) assigned to each partition such that all datums of a given partition
406  * receive the same value. The value for a given partition is the index of
407  * that partition's smallest datum in the all_values[] array.
408  */
409  for (i = 0; i < ndatums; i++)
410  {
411  int orig_index = all_values[i]->index;
412 
413  boundinfo->datums[i] = (Datum *) palloc(sizeof(Datum));
414  boundinfo->datums[i][0] = datumCopy(all_values[i]->value,
415  key->parttypbyval[0],
416  key->parttyplen[0]);
417 
418  /* If the old index has no mapping, assign one */
419  if ((*mapping)[orig_index] == -1)
420  (*mapping)[orig_index] = next_index++;
421 
422  boundinfo->indexes[i] = (*mapping)[orig_index];
423  }
424 
425  /*
426  * Set the canonical value for null_index, if any.
427  *
428  * It is possible that the null-accepting partition has not been assigned
429  * an index yet, which could happen if such partition accepts only null
430  * and hence not handled in the above loop which only looked at non-null
431  * values.
432  */
433  if (null_index != -1)
434  {
435  Assert(null_index >= 0);
436  if ((*mapping)[null_index] == -1)
437  (*mapping)[null_index] = next_index++;
438  boundinfo->null_index = (*mapping)[null_index];
439  }
440 
441  /* Set the canonical value for default_index, if any. */
442  if (default_index != -1)
443  {
444  /*
445  * The default partition accepts any value not specified in the lists
446  * of other partitions, hence it should not get mapped index while
447  * assigning those for non-null datums.
448  */
449  Assert(default_index >= 0);
450  Assert((*mapping)[default_index] == -1);
451  (*mapping)[default_index] = next_index++;
452  boundinfo->default_index = (*mapping)[default_index];
453  }
454 
455  /* All partitions must now have been assigned canonical indexes. */
456  Assert(next_index == nparts);
457  return boundinfo;
458 }
Datum constvalue
Definition: primnodes.h:200
#define NIL
Definition: pg_list.h:65
#define castNode(_type_, nodeptr)
Definition: nodes.h:594
static struct @145 value
#define ERROR
Definition: elog.h:43
char * c
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:130
List * lappend(List *list, void *datum)
Definition: list.c:322
void qsort_arg(void *base, size_t nel, size_t elsize, qsort_arg_comparator cmp, void *arg)
Definition: qsort_arg.c:113
void * palloc0(Size size)
Definition: mcxt.c:980
uintptr_t Datum
Definition: postgres.h:367
bool * parttypbyval
Definition: partcache.h:44
#define Assert(condition)
Definition: c.h:739
#define lfirst(lc)
Definition: pg_list.h:190
int16 * parttyplen
Definition: partcache.h:43
static int list_length(const List *l)
Definition: pg_list.h:169
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:799
void * palloc(Size size)
Definition: mcxt.c:949
#define elog(elevel,...)
Definition: elog.h:228
int i
static int32 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
Definition: partbounds.c:1763
Definition: pg_list.h:50
long val
Definition: informix.c:664
bool constisnull
Definition: primnodes.h:201

◆ create_range_bounds()

static PartitionBoundInfo create_range_bounds ( PartitionBoundSpec **  boundspecs,
int  nparts,
PartitionKey  key,
int **  mapping 
)
static

Definition at line 465 of file partbounds.c.

References Assert, cur, datumCopy(), DatumGetInt32, PartitionBoundInfoData::datums, PartitionRangeBound::datums, PartitionBoundInfoData::default_index, elog, ERROR, FunctionCall2Coll(), i, PartitionRangeBound::index, PartitionBoundInfoData::indexes, PartitionBoundSpec::is_default, PartitionBoundInfoData::kind, PartitionRangeBound::kind, lower(), PartitionBoundSpec::lowerdatums, make_one_partition_rbound(), PartitionBoundInfoData::ndatums, PartitionBoundInfoData::null_index, palloc(), palloc0(), PARTITION_RANGE_DATUM_VALUE, PARTITION_STRATEGY_RANGE, qsort_arg(), qsort_partition_rbound_cmp(), PartitionKeyData::strategy, PartitionBoundInfoData::strategy, PartitionBoundSpec::strategy, upper(), and PartitionBoundSpec::upperdatums.

Referenced by partition_bounds_create().

467 {
468  PartitionBoundInfo boundinfo;
469  PartitionRangeBound **rbounds = NULL;
470  PartitionRangeBound **all_bounds,
471  *prev;
472  int i,
473  k;
474  int ndatums = 0;
475  int default_index = -1;
476  int next_index = 0;
477 
478  boundinfo = (PartitionBoundInfoData *)
480  boundinfo->strategy = key->strategy;
481  /* There is no special null-accepting range partition. */
482  boundinfo->null_index = -1;
483  /* Will be set correctly below. */
484  boundinfo->default_index = -1;
485 
486  all_bounds = (PartitionRangeBound **)
487  palloc0(2 * nparts * sizeof(PartitionRangeBound *));
488 
489  /* Create a unified list of range bounds across all the partitions. */
490  ndatums = 0;
491  for (i = 0; i < nparts; i++)
492  {
493  PartitionBoundSpec *spec = boundspecs[i];
495  *upper;
496 
497  if (spec->strategy != PARTITION_STRATEGY_RANGE)
498  elog(ERROR, "invalid strategy in partition bound spec");
499 
500  /*
501  * Note the index of the partition bound spec for the default
502  * partition. There's no datum to add to the all_bounds array for
503  * this partition.
504  */
505  if (spec->is_default)
506  {
507  default_index = i;
508  continue;
509  }
510 
511  lower = make_one_partition_rbound(key, i, spec->lowerdatums, true);
512  upper = make_one_partition_rbound(key, i, spec->upperdatums, false);
513  all_bounds[ndatums++] = lower;
514  all_bounds[ndatums++] = upper;
515  }
516 
517  Assert(ndatums == nparts * 2 ||
518  (default_index != -1 && ndatums == (nparts - 1) * 2));
519 
520  /* Sort all the bounds in ascending order */
521  qsort_arg(all_bounds, ndatums,
522  sizeof(PartitionRangeBound *),
524  (void *) key);
525 
526  /* Save distinct bounds from all_bounds into rbounds. */
527  rbounds = (PartitionRangeBound **)
528  palloc(ndatums * sizeof(PartitionRangeBound *));
529  k = 0;
530  prev = NULL;
531  for (i = 0; i < ndatums; i++)
532  {
533  PartitionRangeBound *cur = all_bounds[i];
534  bool is_distinct = false;
535  int j;
536 
537  /* Is the current bound distinct from the previous one? */
538  for (j = 0; j < key->partnatts; j++)
539  {
540  Datum cmpval;
541 
542  if (prev == NULL || cur->kind[j] != prev->kind[j])
543  {
544  is_distinct = true;
545  break;
546  }
547 
548  /*
549  * If the bounds are both MINVALUE or MAXVALUE, stop now and treat
550  * them as equal, since any values after this point must be
551  * ignored.
552  */
553  if (cur->kind[j] != PARTITION_RANGE_DATUM_VALUE)
554  break;
555 
556  cmpval = FunctionCall2Coll(&key->partsupfunc[j],
557  key->partcollation[j],
558  cur->datums[j],
559  prev->datums[j]);
560  if (DatumGetInt32(cmpval) != 0)
561  {
562  is_distinct = true;
563  break;
564  }
565  }
566 
567  /*
568  * Only if the bound is distinct save it into a temporary array, i.e,
569  * rbounds which is later copied into boundinfo datums array.
570  */
571  if (is_distinct)
572  rbounds[k++] = all_bounds[i];
573 
574  prev = cur;
575  }
576 
577  /* Update ndatums to hold the count of distinct datums. */
578  ndatums = k;
579 
580  /*
581  * Add datums to boundinfo. Canonical indexes are values ranging from 0
582  * to nparts - 1, assigned in that order to each partition's upper bound.
583  * For 'datums' elements that are lower bounds, there is -1 in the
584  * 'indexes' array to signify that no partition exists for the values less
585  * than such a bound and greater than or equal to the previous upper
586  * bound.
587  */
588  boundinfo->ndatums = ndatums;
589  boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *));
590  boundinfo->kind = (PartitionRangeDatumKind **)
591  palloc(ndatums *
592  sizeof(PartitionRangeDatumKind *));
593 
594  /*
595  * For range partitioning, an additional value of -1 is stored as the last
596  * element.
597  */
598  boundinfo->indexes = (int *) palloc((ndatums + 1) * sizeof(int));
599 
600  for (i = 0; i < ndatums; i++)
601  {
602  int j;
603 
604  boundinfo->datums[i] = (Datum *) palloc(key->partnatts *
605  sizeof(Datum));
606  boundinfo->kind[i] = (PartitionRangeDatumKind *)
607  palloc(key->partnatts *
608  sizeof(PartitionRangeDatumKind));
609  for (j = 0; j < key->partnatts; j++)
610  {
611  if (rbounds[i]->kind[j] == PARTITION_RANGE_DATUM_VALUE)
612  boundinfo->datums[i][j] =
613  datumCopy(rbounds[i]->datums[j],
614  key->parttypbyval[j],
615  key->parttyplen[j]);
616  boundinfo->kind[i][j] = rbounds[i]->kind[j];
617  }
618 
619  /*
620  * There is no mapping for invalid indexes.
621  *
622  * Any lower bounds in the rbounds array have invalid indexes
623  * assigned, because the values between the previous bound (if there
624  * is one) and this (lower) bound are not part of the range of any
625  * existing partition.
626  */
627  if (rbounds[i]->lower)
628  boundinfo->indexes[i] = -1;
629  else
630  {
631  int orig_index = rbounds[i]->index;
632 
633  /* If the old index has no mapping, assign one */
634  if ((*mapping)[orig_index] == -1)
635  (*mapping)[orig_index] = next_index++;
636 
637  boundinfo->indexes[i] = (*mapping)[orig_index];
638  }
639  }
640 
641  /* Set the canonical value for default_index, if any. */
642  if (default_index != -1)
643  {
644  Assert(default_index >= 0 && (*mapping)[default_index] == -1);
645  (*mapping)[default_index] = next_index++;
646  boundinfo->default_index = (*mapping)[default_index];
647  }
648 
649  /* The extra -1 element. */
650  Assert(i == ndatums);
651  boundinfo->indexes[i] = -1;
652 
653  /* All partitions must now have been assigned canonical indexes. */
654  Assert(next_index == nparts);
655  return boundinfo;
656 }
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
PartitionRangeDatumKind * kind
Definition: partbounds.c:68
#define DatumGetInt32(X)
Definition: postgres.h:472
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:43
FmgrInfo * partsupfunc
Definition: partcache.h:35
PartitionRangeDatumKind
Definition: parsenodes.h:834
struct cursor * cur
Definition: ecpg.c:28
static int32 qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
Definition: partbounds.c:1780
Datum upper(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:74
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1150
static PartitionRangeBound * make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
Definition: partbounds.c:1405
#define ERROR
Definition: elog.h:43
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:130
void qsort_arg(void *base, size_t nel, size_t elsize, qsort_arg_comparator cmp, void *arg)
Definition: qsort_arg.c:113
Oid * partcollation
Definition: partcache.h:38
void * palloc0(Size size)
Definition: mcxt.c:980
uintptr_t Datum
Definition: postgres.h:367
bool * parttypbyval
Definition: partcache.h:44
#define Assert(condition)
Definition: c.h:739
int16 * parttyplen
Definition: partcache.h:43
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:800
void * palloc(Size size)
Definition: mcxt.c:949
#define elog(elevel,...)
Definition: elog.h:228
int i

◆ get_hash_partition_greatest_modulus()

int get_hash_partition_greatest_modulus ( PartitionBoundInfo  bound)

Definition at line 1388 of file partbounds.c.

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

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

1389 {
1390  Assert(bound && bound->strategy == PARTITION_STRATEGY_HASH);
1391  Assert(bound->datums && bound->ndatums > 0);
1392  Assert(DatumGetInt32(bound->datums[bound->ndatums - 1][0]) > 0);
1393 
1394  return DatumGetInt32(bound->datums[bound->ndatums - 1][0]);
1395 }
#define DatumGetInt32(X)
Definition: postgres.h:472
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:798
#define Assert(condition)
Definition: c.h:739

◆ get_partition_bound_num_indexes()

static int get_partition_bound_num_indexes ( PartitionBoundInfo  b)
static

Definition at line 1797 of file partbounds.c.

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

Referenced by partition_bounds_copy().

1798 {
1799  int num_indexes;
1800 
1801  Assert(bound);
1802 
1803  switch (bound->strategy)
1804  {
1806 
1807  /*
1808  * The number of the entries in the indexes array is same as the
1809  * greatest modulus.
1810  */
1811  num_indexes = get_hash_partition_greatest_modulus(bound);
1812  break;
1813 
1815  num_indexes = bound->ndatums;
1816  break;
1817 
1819  /* Range partitioned table has an extra index. */
1820  num_indexes = bound->ndatums + 1;
1821  break;
1822 
1823  default:
1824  elog(ERROR, "unexpected partition strategy: %d",
1825  (int) bound->strategy);
1826  }
1827 
1828  return num_indexes;
1829 }
#define ERROR
Definition: elog.h:43
int get_hash_partition_greatest_modulus(PartitionBoundInfo bound)
Definition: partbounds.c:1388
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:798
#define Assert(condition)
Definition: c.h:739
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:799
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:800
#define elog(elevel,...)
Definition: elog.h:228

◆ get_partition_operator()

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

Definition at line 1842 of file partbounds.c.

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

Referenced by make_partition_op_expr().

1844 {
1845  Oid operoid;
1846 
1847  /*
1848  * Get the operator in the partitioning opfamily using the opclass'
1849  * declared input type as both left- and righttype.
1850  */
1851  operoid = get_opfamily_member(key->partopfamily[col],
1852  key->partopcintype[col],
1853  key->partopcintype[col],
1854  strategy);
1855  if (!OidIsValid(operoid))
1856  elog(ERROR, "missing operator %d(%u,%u) in partition opfamily %u",
1857  strategy, key->partopcintype[col], key->partopcintype[col],
1858  key->partopfamily[col]);
1859 
1860  /*
1861  * If the partition key column is not of the same type as the operator
1862  * class and not polymorphic, tell caller to wrap the non-Const expression
1863  * in a RelabelType. This matches what parse_coerce.c does.
1864  */
1865  *need_relabel = (key->parttypid[col] != key->partopcintype[col] &&
1866  key->partopcintype[col] != RECORDOID &&
1867  !IsPolymorphicType(key->partopcintype[col]));
1868 
1869  return operoid;
1870 }
Oid * partopfamily
Definition: partcache.h:33
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:645
#define ERROR
Definition: elog.h:43
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:163
Oid * partopcintype
Definition: partcache.h:34
#define elog(elevel,...)
Definition: elog.h:228

◆ get_qual_for_hash()

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

Definition at line 1991 of file partbounds.c.

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

Referenced by get_qual_from_partbound().

1992 {
1994  FuncExpr *fexpr;
1995  Node *relidConst;
1996  Node *modulusConst;
1997  Node *remainderConst;
1998  List *args;
1999  ListCell *partexprs_item;
2000  int i;
2001 
2002  /* Fixed arguments. */
2003  relidConst = (Node *) makeConst(OIDOID,
2004  -1,
2005  InvalidOid,
2006  sizeof(Oid),
2008  false,
2009  true);
2010 
2011  modulusConst = (Node *) makeConst(INT4OID,
2012  -1,
2013  InvalidOid,
2014  sizeof(int32),
2015  Int32GetDatum(spec->modulus),
2016  false,
2017  true);
2018 
2019  remainderConst = (Node *) makeConst(INT4OID,
2020  -1,
2021  InvalidOid,
2022  sizeof(int32),
2023  Int32GetDatum(spec->remainder),
2024  false,
2025  true);
2026 
2027  args = list_make3(relidConst, modulusConst, remainderConst);
2028  partexprs_item = list_head(key->partexprs);
2029 
2030  /* Add an argument for each key column. */
2031  for (i = 0; i < key->partnatts; i++)
2032  {
2033  Node *keyCol;
2034 
2035  /* Left operand */
2036  if (key->partattrs[i] != 0)
2037  {
2038  keyCol = (Node *) makeVar(1,
2039  key->partattrs[i],
2040  key->parttypid[i],
2041  key->parttypmod[i],
2042  key->parttypcoll[i],
2043  0);
2044  }
2045  else
2046  {
2047  keyCol = (Node *) copyObject(lfirst(partexprs_item));
2048  partexprs_item = lnext(key->partexprs, partexprs_item);
2049  }
2050 
2051  args = lappend(args, keyCol);
2052  }
2053 
2054  fexpr = makeFuncExpr(F_SATISFIES_HASH_PARTITION,
2055  BOOLOID,
2056  args,
2057  InvalidOid,
2058  InvalidOid,
2060 
2061  return list_make1(fexpr);
2062 }
#define list_make3(x1, x2, x3)
Definition: pg_list.h:231
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:321
Definition: nodes.h:525
List * partexprs
Definition: partcache.h:30
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:297
signed int int32
Definition: c.h:347
#define list_make1(x1)
Definition: pg_list.h:227
Oid * parttypcoll
Definition: partcache.h:46
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
List * lappend(List *list, void *datum)
Definition: list.c:322
AttrNumber * partattrs
Definition: partcache.h:28
int32 * parttypmod
Definition: partcache.h:42
#define InvalidOid
Definition: postgres_ext.h:36
#define lfirst(lc)
Definition: pg_list.h:190
#define RelationGetPartitionKey(relation)
Definition: rel.h:603
#define Int32GetDatum(X)
Definition: postgres.h:479
int i
#define copyObject(obj)
Definition: nodes.h:641
Definition: pg_list.h:50
#define RelationGetRelid(relation)
Definition: rel.h:422
FuncExpr * makeFuncExpr(Oid funcid, Oid rettype, List *args, Oid funccollid, Oid inputcollid, CoercionForm fformat)
Definition: makefuncs.c:517

◆ get_qual_for_list()

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

Definition at line 2074 of file partbounds.c.

References NullTest::arg, NullTest::argisrow, Assert, PartitionDescData::boundinfo, BTEqualStrategyNumber, castNode, Const::constisnull, copyObject, datumCopy(), PartitionBoundInfoData::datums, i, PartitionBoundSpec::is_default, IS_NOT_NULL, IS_NULL, sort-test::key, lappend(), lfirst, linitial, list_make1, list_make2, PartitionBoundSpec::listdatums, NullTest::location, make_ands_explicit(), make_partition_op_expr(), makeBoolExpr(), makeConst(), makeNode, makeVar(), 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, and val.

Referenced by check_default_partition_contents(), and get_qual_from_partbound().

2075 {
2077  List *result;
2078  Expr *keyCol;
2079  Expr *opexpr;
2080  NullTest *nulltest;
2081  ListCell *cell;
2082  List *elems = NIL;
2083  bool list_has_null = false;
2084 
2085  /*
2086  * Only single-column list partitioning is supported, so we are worried
2087  * only about the partition key with index 0.
2088  */
2089  Assert(key->partnatts == 1);
2090 
2091  /* Construct Var or expression representing the partition column */
2092  if (key->partattrs[0] != 0)
2093  keyCol = (Expr *) makeVar(1,
2094  key->partattrs[0],
2095  key->parttypid[0],
2096  key->parttypmod[0],
2097  key->parttypcoll[0],
2098  0);
2099  else
2100  keyCol = (Expr *) copyObject(linitial(key->partexprs));
2101 
2102  /*
2103  * For default list partition, collect datums for all the partitions. The
2104  * default partition constraint should check that the partition key is
2105  * equal to none of those.
2106  */
2107  if (spec->is_default)
2108  {
2109  int i;
2110  int ndatums = 0;
2111  PartitionDesc pdesc = RelationGetPartitionDesc(parent);
2112  PartitionBoundInfo boundinfo = pdesc->boundinfo;
2113 
2114  if (boundinfo)
2115  {
2116  ndatums = boundinfo->ndatums;
2117 
2118  if (partition_bound_accepts_nulls(boundinfo))
2119  list_has_null = true;
2120  }
2121 
2122  /*
2123  * If default is the only partition, there need not be any partition
2124  * constraint on it.
2125  */
2126  if (ndatums == 0 && !list_has_null)
2127  return NIL;
2128 
2129  for (i = 0; i < ndatums; i++)
2130  {
2131  Const *val;
2132 
2133  /*
2134  * Construct Const from known-not-null datum. We must be careful
2135  * to copy the value, because our result has to be able to outlive
2136  * the relcache entry we're copying from.
2137  */
2138  val = makeConst(key->parttypid[0],
2139  key->parttypmod[0],
2140  key->parttypcoll[0],
2141  key->parttyplen[0],
2142  datumCopy(*boundinfo->datums[i],
2143  key->parttypbyval[0],
2144  key->parttyplen[0]),
2145  false, /* isnull */
2146  key->parttypbyval[0]);
2147 
2148  elems = lappend(elems, val);
2149  }
2150  }
2151  else
2152  {
2153  /*
2154  * Create list of Consts for the allowed values, excluding any nulls.
2155  */
2156  foreach(cell, spec->listdatums)
2157  {
2158  Const *val = castNode(Const, lfirst(cell));
2159 
2160  if (val->constisnull)
2161  list_has_null = true;
2162  else
2163  elems = lappend(elems, copyObject(val));
2164  }
2165  }
2166 
2167  if (elems)
2168  {
2169  /*
2170  * Generate the operator expression from the non-null partition
2171  * values.
2172  */
2174  keyCol, (Expr *) elems);
2175  }
2176  else
2177  {
2178  /*
2179  * If there are no partition values, we don't need an operator
2180  * expression.
2181  */
2182  opexpr = NULL;
2183  }
2184 
2185  if (!list_has_null)
2186  {
2187  /*
2188  * Gin up a "col IS NOT NULL" test that will be AND'd with the main
2189  * expression. This might seem redundant, but the partition routing
2190  * machinery needs it.
2191  */
2192  nulltest = makeNode(NullTest);
2193  nulltest->arg = keyCol;
2194  nulltest->nulltesttype = IS_NOT_NULL;
2195  nulltest->argisrow = false;
2196  nulltest->location = -1;
2197 
2198  result = opexpr ? list_make2(nulltest, opexpr) : list_make1(nulltest);
2199  }
2200  else
2201  {
2202  /*
2203  * Gin up a "col IS NULL" test that will be OR'd with the main
2204  * expression.
2205  */
2206  nulltest = makeNode(NullTest);
2207  nulltest->arg = keyCol;
2208  nulltest->nulltesttype = IS_NULL;
2209  nulltest->argisrow = false;
2210  nulltest->location = -1;
2211 
2212  if (opexpr)
2213  {
2214  Expr *or;
2215 
2216  or = makeBoolExpr(OR_EXPR, list_make2(nulltest, opexpr), -1);
2217  result = list_make1(or);
2218  }
2219  else
2220  result = list_make1(nulltest);
2221  }
2222 
2223  /*
2224  * Note that, in general, applying NOT to a constraint expression doesn't
2225  * necessarily invert the set of rows it accepts, because NOT (NULL) is
2226  * NULL. However, the partition constraints we construct here never
2227  * evaluate to NULL, so applying NOT works as intended.
2228  */
2229  if (spec->is_default)
2230  {
2231  result = list_make1(make_ands_explicit(result));
2232  result = list_make1(makeBoolExpr(NOT_EXPR, result, -1));
2233  }
2234 
2235  return result;
2236 }
#define list_make2(x1, x2)
Definition: pg_list.h:229
#define NIL
Definition: pg_list.h:65
#define castNode(_type_, nodeptr)
Definition: nodes.h:594
List * partexprs
Definition: partcache.h:30
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:297
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:367
PartitionBoundInfo boundinfo
Definition: partdesc.h:29
#define list_make1(x1)
Definition: pg_list.h:227
static Expr * make_partition_op_expr(PartitionKey key, int keynum, uint16 strategy, Expr *arg1, Expr *arg2)
Definition: partbounds.c:1878
Oid * parttypcoll
Definition: partcache.h:46
#define linitial(l)
Definition: pg_list.h:195
Expr * arg
Definition: primnodes.h:1205
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:130
List * lappend(List *list, void *datum)
Definition: list.c:322
AttrNumber * partattrs
Definition: partcache.h:28
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:705
NullTestType nulltesttype
Definition: primnodes.h:1206
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:74
int32 * parttypmod
Definition: partcache.h:42
#define makeNode(_type_)
Definition: nodes.h:573
bool * parttypbyval
Definition: partcache.h:44
#define Assert(condition)
Definition: c.h:739
#define lfirst(lc)
Definition: pg_list.h:190
int16 * parttyplen
Definition: partcache.h:43
int location
Definition: primnodes.h:1208
#define RelationGetPartitionKey(relation)
Definition: rel.h:603
int i
bool argisrow
Definition: primnodes.h:1207
#define copyObject(obj)
Definition: nodes.h:641
Definition: pg_list.h:50
long val
Definition: informix.c:664
bool constisnull
Definition: primnodes.h:201
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define RelationGetPartitionDesc(relation)
Definition: rel.h:609

◆ get_qual_for_range()

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

Definition at line 2283 of file partbounds.c.

References AND_EXPR, 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, sort-test::key, PartitionRangeDatum::kind, lappend(), lfirst, linitial, list_head(), list_length(), list_make1, lnext(), PartitionBoundSpec::lowerdatums, make_partition_op_expr(), makeBoolConst(), makeBoolExpr(), MemoryContextSwitchTo(), NIL, NOT_EXPR, PartitionDescData::nparts, PartitionDescData::oids, OR_EXPR, PartitionKeyData::partexprs, PARTITION_RANGE_DATUM_MAXVALUE, PARTITION_RANGE_DATUM_MINVALUE, PARTITION_RANGE_DATUM_VALUE, PartitionKeyData::partnatts, RelationGetPartitionDesc, RelationGetPartitionKey, ReleaseSysCache(), RELOID, SearchSysCache1(), stringToNode(), SysCacheGetAttr(), TextDatumGetCString, and PartitionBoundSpec::upperdatums.

Referenced by check_default_partition_contents(), and get_qual_from_partbound().

2285 {
2286  List *result = NIL;
2287  ListCell *cell1,
2288  *cell2,
2289  *partexprs_item,
2290  *partexprs_item_saved;
2291  int i,
2292  j;
2293  PartitionRangeDatum *ldatum,
2294  *udatum;
2296  Expr *keyCol;
2297  Const *lower_val,
2298  *upper_val;
2299  List *lower_or_arms,
2300  *upper_or_arms;
2301  int num_or_arms,
2302  current_or_arm;
2303  ListCell *lower_or_start_datum,
2304  *upper_or_start_datum;
2305  bool need_next_lower_arm,
2306  need_next_upper_arm;
2307 
2308  if (spec->is_default)
2309  {
2310  List *or_expr_args = NIL;
2311  PartitionDesc pdesc = RelationGetPartitionDesc(parent);
2312  Oid *inhoids = pdesc->oids;
2313  int nparts = pdesc->nparts,
2314  i;
2315 
2316  for (i = 0; i < nparts; i++)
2317  {
2318  Oid inhrelid = inhoids[i];
2319  HeapTuple tuple;
2320  Datum datum;
2321  bool isnull;
2322  PartitionBoundSpec *bspec;
2323 
2324  tuple = SearchSysCache1(RELOID, inhrelid);
2325  if (!HeapTupleIsValid(tuple))
2326  elog(ERROR, "cache lookup failed for relation %u", inhrelid);
2327 
2328  datum = SysCacheGetAttr(RELOID, tuple,
2329  Anum_pg_class_relpartbound,
2330  &isnull);
2331  if (isnull)
2332  elog(ERROR, "null relpartbound for relation %u", inhrelid);
2333 
2334  bspec = (PartitionBoundSpec *)
2336  if (!IsA(bspec, PartitionBoundSpec))
2337  elog(ERROR, "expected PartitionBoundSpec");
2338 
2339  if (!bspec->is_default)
2340  {
2341  List *part_qual;
2342 
2343  part_qual = get_qual_for_range(parent, bspec, true);
2344 
2345  /*
2346  * AND the constraints of the partition and add to
2347  * or_expr_args
2348  */
2349  or_expr_args = lappend(or_expr_args, list_length(part_qual) > 1
2350  ? makeBoolExpr(AND_EXPR, part_qual, -1)
2351  : linitial(part_qual));
2352  }
2353  ReleaseSysCache(tuple);
2354  }
2355 
2356  if (or_expr_args != NIL)
2357  {
2358  Expr *other_parts_constr;
2359 
2360  /*
2361  * Combine the constraints obtained for non-default partitions
2362  * using OR. As requested, each of the OR's args doesn't include
2363  * the NOT NULL test for partition keys (which is to avoid its
2364  * useless repetition). Add the same now.
2365  */
2366  other_parts_constr =
2369  list_length(or_expr_args) > 1
2370  ? makeBoolExpr(OR_EXPR, or_expr_args,
2371  -1)
2372  : linitial(or_expr_args)),
2373  -1);
2374 
2375  /*
2376  * Finally, the default partition contains everything *NOT*
2377  * contained in the non-default partitions.
2378  */
2379  result = list_make1(makeBoolExpr(NOT_EXPR,
2380  list_make1(other_parts_constr), -1));
2381  }
2382 
2383  return result;
2384  }
2385 
2386  lower_or_start_datum = list_head(spec->lowerdatums);
2387  upper_or_start_datum = list_head(spec->upperdatums);
2388  num_or_arms = key->partnatts;
2389 
2390  /*
2391  * If it is the recursive call for default, we skip the get_range_nulltest
2392  * to avoid accumulating the NullTest on the same keys for each partition.
2393  */
2394  if (!for_default)
2395  result = get_range_nulltest(key);
2396 
2397  /*
2398  * Iterate over the key columns and check if the corresponding lower and
2399  * upper datums are equal using the btree equality operator for the
2400  * column's type. If equal, we emit single keyCol = common_value
2401  * expression. Starting from the first column for which the corresponding
2402  * lower and upper bound datums are not equal, we generate OR expressions
2403  * as shown in the function's header comment.
2404  */
2405  i = 0;
2406  partexprs_item = list_head(key->partexprs);
2407  partexprs_item_saved = partexprs_item; /* placate compiler */
2408  forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
2409  {
2410  EState *estate;
2411  MemoryContext oldcxt;
2412  Expr *test_expr;
2413  ExprState *test_exprstate;
2414  Datum test_result;
2415  bool isNull;
2416 
2417  ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
2418  udatum = castNode(PartitionRangeDatum, lfirst(cell2));
2419 
2420  /*
2421  * Since get_range_key_properties() modifies partexprs_item, and we
2422  * might need to start over from the previous expression in the later
2423  * part of this function, save away the current value.
2424  */
2425  partexprs_item_saved = partexprs_item;
2426 
2427  get_range_key_properties(key, i, ldatum, udatum,
2428  &partexprs_item,
2429  &keyCol,
2430  &lower_val, &upper_val);
2431 
2432  /*
2433  * If either value is NULL, the corresponding partition bound is
2434  * either MINVALUE or MAXVALUE, and we treat them as unequal, because
2435  * even if they're the same, there is no common value to equate the
2436  * key column with.
2437  */
2438  if (!lower_val || !upper_val)
2439  break;
2440 
2441  /* Create the test expression */
2442  estate = CreateExecutorState();
2443  oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
2444  test_expr = make_partition_op_expr(key, i, BTEqualStrategyNumber,
2445  (Expr *) lower_val,
2446  (Expr *) upper_val);
2447  fix_opfuncids((Node *) test_expr);
2448  test_exprstate = ExecInitExpr(test_expr, NULL);
2449  test_result = ExecEvalExprSwitchContext(test_exprstate,
2450  GetPerTupleExprContext(estate),
2451  &isNull);
2452  MemoryContextSwitchTo(oldcxt);
2453  FreeExecutorState(estate);
2454 
2455  /* If not equal, go generate the OR expressions */
2456  if (!DatumGetBool(test_result))
2457  break;
2458 
2459  /*
2460  * The bounds for the last key column can't be equal, because such a
2461  * range partition would never be allowed to be defined (it would have
2462  * an empty range otherwise).
2463  */
2464  if (i == key->partnatts - 1)
2465  elog(ERROR, "invalid range bound specification");
2466 
2467  /* Equal, so generate keyCol = lower_val expression */
2468  result = lappend(result,
2470  keyCol, (Expr *) lower_val));
2471 
2472  i++;
2473  }
2474 
2475  /* First pair of lower_val and upper_val that are not equal. */
2476  lower_or_start_datum = cell1;
2477  upper_or_start_datum = cell2;
2478 
2479  /* OR will have as many arms as there are key columns left. */
2480  num_or_arms = key->partnatts - i;
2481  current_or_arm = 0;
2482  lower_or_arms = upper_or_arms = NIL;
2483  need_next_lower_arm = need_next_upper_arm = true;
2484  while (current_or_arm < num_or_arms)
2485  {
2486  List *lower_or_arm_args = NIL,
2487  *upper_or_arm_args = NIL;
2488 
2489  /* Restart scan of columns from the i'th one */
2490  j = i;
2491  partexprs_item = partexprs_item_saved;
2492 
2493  for_both_cell(cell1, spec->lowerdatums, lower_or_start_datum,
2494  cell2, spec->upperdatums, upper_or_start_datum)
2495  {
2496  PartitionRangeDatum *ldatum_next = NULL,
2497  *udatum_next = NULL;
2498 
2499  ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
2500  if (lnext(spec->lowerdatums, cell1))
2501  ldatum_next = castNode(PartitionRangeDatum,
2502  lfirst(lnext(spec->lowerdatums, cell1)));
2503  udatum = castNode(PartitionRangeDatum, lfirst(cell2));
2504  if (lnext(spec->upperdatums, cell2))
2505  udatum_next = castNode(PartitionRangeDatum,
2506  lfirst(lnext(spec->upperdatums, cell2)));
2507  get_range_key_properties(key, j, ldatum, udatum,
2508  &partexprs_item,
2509  &keyCol,
2510  &lower_val, &upper_val);
2511 
2512  if (need_next_lower_arm && lower_val)
2513  {
2514  uint16 strategy;
2515 
2516  /*
2517  * For the non-last columns of this arm, use the EQ operator.
2518  * For the last column of this arm, use GT, unless this is the
2519  * last column of the whole bound check, or the next bound
2520  * datum is MINVALUE, in which case use GE.
2521  */
2522  if (j - i < current_or_arm)
2523  strategy = BTEqualStrategyNumber;
2524  else if (j == key->partnatts - 1 ||
2525  (ldatum_next &&
2526  ldatum_next->kind == PARTITION_RANGE_DATUM_MINVALUE))
2527  strategy = BTGreaterEqualStrategyNumber;
2528  else
2529  strategy = BTGreaterStrategyNumber;
2530 
2531  lower_or_arm_args = lappend(lower_or_arm_args,
2532  make_partition_op_expr(key, j,
2533  strategy,
2534  keyCol,
2535  (Expr *) lower_val));
2536  }
2537 
2538  if (need_next_upper_arm && upper_val)
2539  {
2540  uint16 strategy;
2541 
2542  /*
2543  * For the non-last columns of this arm, use the EQ operator.
2544  * For the last column of this arm, use LT, unless the next
2545  * bound datum is MAXVALUE, in which case use LE.
2546  */
2547  if (j - i < current_or_arm)
2548  strategy = BTEqualStrategyNumber;
2549  else if (udatum_next &&
2550  udatum_next->kind == PARTITION_RANGE_DATUM_MAXVALUE)
2551  strategy = BTLessEqualStrategyNumber;
2552  else
2553  strategy = BTLessStrategyNumber;
2554 
2555  upper_or_arm_args = lappend(upper_or_arm_args,
2556  make_partition_op_expr(key, j,
2557  strategy,
2558  keyCol,
2559  (Expr *) upper_val));
2560  }
2561 
2562  /*
2563  * Did we generate enough of OR's arguments? First arm considers
2564  * the first of the remaining columns, second arm considers first
2565  * two of the remaining columns, and so on.
2566  */
2567  ++j;
2568  if (j - i > current_or_arm)
2569  {
2570  /*
2571  * We must not emit any more arms if the new column that will
2572  * be considered is unbounded, or this one was.
2573  */
2574  if (!lower_val || !ldatum_next ||
2575  ldatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
2576  need_next_lower_arm = false;
2577  if (!upper_val || !udatum_next ||
2578  udatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
2579  need_next_upper_arm = false;
2580  break;
2581  }
2582  }
2583 
2584  if (lower_or_arm_args != NIL)
2585  lower_or_arms = lappend(lower_or_arms,
2586  list_length(lower_or_arm_args) > 1
2587  ? makeBoolExpr(AND_EXPR, lower_or_arm_args, -1)
2588  : linitial(lower_or_arm_args));
2589 
2590  if (upper_or_arm_args != NIL)
2591  upper_or_arms = lappend(upper_or_arms,
2592  list_length(upper_or_arm_args) > 1
2593  ? makeBoolExpr(AND_EXPR, upper_or_arm_args, -1)
2594  : linitial(upper_or_arm_args));
2595 
2596  /* If no work to do in the next iteration, break away. */
2597  if (!need_next_lower_arm && !need_next_upper_arm)
2598  break;
2599 
2600  ++current_or_arm;
2601  }
2602 
2603  /*
2604  * Generate the OR expressions for each of lower and upper bounds (if
2605  * required), and append to the list of implicitly ANDed list of
2606  * expressions.
2607  */
2608  if (lower_or_arms != NIL)
2609  result = lappend(result,
2610  list_length(lower_or_arms) > 1
2611  ? makeBoolExpr(OR_EXPR, lower_or_arms, -1)
2612  : linitial(lower_or_arms));
2613  if (upper_or_arms != NIL)
2614  result = lappend(result,
2615  list_length(upper_or_arms) > 1
2616  ? makeBoolExpr(OR_EXPR, upper_or_arms, -1)
2617  : linitial(upper_or_arms));
2618 
2619  /*
2620  * As noted above, for non-default, we return list with constant TRUE. If
2621  * the result is NIL during the recursive call for default, it implies
2622  * this is the only other partition which can hold every value of the key
2623  * except NULL. Hence we return the NullTest result skipped earlier.
2624  */
2625  if (result == NIL)
2626  result = for_default
2627  ? get_range_nulltest(key)
2629 
2630  return result;
2631 }
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:300
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:419
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:321
#define castNode(_type_, nodeptr)
Definition: nodes.h:594
void fix_opfuncids(Node *node)
Definition: nodeFuncs.c:1587
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
PartitionRangeDatumKind kind
Definition: parsenodes.h:845
Definition: nodes.h:525
static List * get_range_nulltest(PartitionKey key)
Definition: partbounds.c:2693
void * stringToNode(const char *str)
Definition: read.c:89
#define false
Definition: c.h:317
List * partexprs
Definition: partcache.h:30
unsigned int Oid
Definition: postgres_ext.h:31
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:367
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
#define list_make1(x1)
Definition: pg_list.h:227
void FreeExecutorState(EState *estate)
Definition: execUtils.c:190
static Expr * make_partition_op_expr(PartitionKey key, int keynum, uint16 strategy, Expr *arg1, Expr *arg2)
Definition: partbounds.c:1878
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: partbounds.c:2649
#define GetPerTupleExprContext(estate)
Definition: executor.h:501
#define true
Definition: c.h:313
unsigned short uint16
Definition: c.h:358
MemoryContext es_query_cxt
Definition: execnodes.h:549
#define linitial(l)
Definition: pg_list.h:195
#define ERROR
Definition: elog.h:43
Node * makeBoolConst(bool value, bool isnull)
Definition: makefuncs.c:355
#define DatumGetBool(X)
Definition: postgres.h:393
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
EState * CreateExecutorState(void)
Definition: execUtils.c:88
List * lappend(List *list, void *datum)
Definition: list.c:322
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1116
#define TextDatumGetCString(d)
Definition: builtins.h:84
uintptr_t Datum
Definition: postgres.h:367
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1164
#define for_both_cell(cell1, list1, initcell1, cell2, list2, initcell2)
Definition: pg_list.h:441
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1377
static List * get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default)
Definition: partbounds.c:2283
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define lfirst(lc)
Definition: pg_list.h:190
static int list_length(const List *l)
Definition: pg_list.h:169
#define RelationGetPartitionKey(relation)
Definition: rel.h:603
#define elog(elevel,...)
Definition: elog.h:228
int i
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:121
#define BTLessStrategyNumber
Definition: stratnum.h:29
Definition: pg_list.h:50
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32
#define RelationGetPartitionDesc(relation)
Definition: rel.h:609

◆ get_qual_from_partbound()

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

Definition at line 118 of file partbounds.c.

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

Referenced by ATExecAttachPartition(), and generate_partition_qual().

120 {
122  List *my_qual = NIL;
123 
124  Assert(key != NULL);
125 
126  switch (key->strategy)
127  {
130  my_qual = get_qual_for_hash(parent, spec);
131  break;
132 
135  my_qual = get_qual_for_list(parent, spec);
136  break;
137 
140  my_qual = get_qual_for_range(parent, spec, false);
141  break;
142 
143  default:
144  elog(ERROR, "unexpected partition strategy: %d",
145  (int) key->strategy);
146  }
147 
148  return my_qual;
149 }
#define NIL
Definition: pg_list.h:65
static List * get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:1991
#define ERROR
Definition: elog.h:43
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:798
static List * get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default)
Definition: partbounds.c:2283
#define Assert(condition)
Definition: c.h:739
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:799
#define RelationGetPartitionKey(relation)
Definition: rel.h:603
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:800
#define elog(elevel,...)
Definition: elog.h:228
Definition: pg_list.h:50
static List * get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:2074

◆ 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 2649 of file partbounds.c.

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

Referenced by get_qual_for_range().

2655 {
2656  /* Get partition key expression for this column */
2657  if (key->partattrs[keynum] != 0)
2658  {
2659  *keyCol = (Expr *) makeVar(1,
2660  key->partattrs[keynum],
2661  key->parttypid[keynum],
2662  key->parttypmod[keynum],
2663  key->parttypcoll[keynum],
2664  0);
2665  }
2666  else
2667  {
2668  if (*partexprs_item == NULL)
2669  elog(ERROR, "wrong number of partition key expressions");
2670  *keyCol = copyObject(lfirst(*partexprs_item));
2671  *partexprs_item = lnext(key->partexprs, *partexprs_item);
2672  }
2673 
2674  /* Get appropriate Const nodes for the bounds */
2675  if (ldatum->kind == PARTITION_RANGE_DATUM_VALUE)
2676  *lower_val = castNode(Const, copyObject(ldatum->value));
2677  else
2678  *lower_val = NULL;
2679 
2680  if (udatum->kind == PARTITION_RANGE_DATUM_VALUE)
2681  *upper_val = castNode(Const, copyObject(udatum->value));
2682  else
2683  *upper_val = NULL;
2684 }
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:321
#define castNode(_type_, nodeptr)
Definition: nodes.h:594
PartitionRangeDatumKind kind
Definition: parsenodes.h:845
List * partexprs
Definition: partcache.h:30
Oid * parttypcoll
Definition: partcache.h:46
#define ERROR
Definition: elog.h:43
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
AttrNumber * partattrs
Definition: partcache.h:28
int32 * parttypmod
Definition: partcache.h:42
#define lfirst(lc)
Definition: pg_list.h:190
#define elog(elevel,...)
Definition: elog.h:228
#define copyObject(obj)
Definition: nodes.h:641

◆ get_range_nulltest()

static List * get_range_nulltest ( PartitionKey  key)
static

Definition at line 2693 of file partbounds.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().

2694 {
2695  List *result = NIL;
2696  NullTest *nulltest;
2697  ListCell *partexprs_item;
2698  int i;
2699 
2700  partexprs_item = list_head(key->partexprs);
2701  for (i = 0; i < key->partnatts; i++)
2702  {
2703  Expr *keyCol;
2704 
2705  if (key->partattrs[i] != 0)
2706  {
2707  keyCol = (Expr *) makeVar(1,
2708  key->partattrs[i],
2709  key->parttypid[i],
2710  key->parttypmod[i],
2711  key->parttypcoll[i],
2712  0);
2713  }
2714  else
2715  {
2716  if (partexprs_item == NULL)
2717  elog(ERROR, "wrong number of partition key expressions");
2718  keyCol = copyObject(lfirst(partexprs_item));
2719  partexprs_item = lnext(key->partexprs, partexprs_item);
2720  }
2721 
2722  nulltest = makeNode(NullTest);
2723  nulltest->arg = keyCol;
2724  nulltest->nulltesttype = IS_NOT_NULL;
2725  nulltest->argisrow = false;
2726  nulltest->location = -1;
2727  result = lappend(result, nulltest);
2728  }
2729 
2730  return result;
2731 }
#define NIL
Definition: pg_list.h:65
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:321
List * partexprs
Definition: partcache.h:30
Oid * parttypcoll
Definition: partcache.h:46
#define ERROR
Definition: elog.h:43
Expr * arg
Definition: primnodes.h:1205
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
List * lappend(List *list, void *datum)
Definition: list.c:322
AttrNumber * partattrs
Definition: partcache.h:28
NullTestType nulltesttype
Definition: primnodes.h:1206
int32 * parttypmod
Definition: partcache.h:42
#define makeNode(_type_)
Definition: nodes.h:573
#define lfirst(lc)
Definition: pg_list.h:190
int location
Definition: primnodes.h:1208
#define elog(elevel,...)
Definition: elog.h:228
int i
bool argisrow
Definition: primnodes.h:1207
#define copyObject(obj)
Definition: nodes.h:641
Definition: pg_list.h:50

◆ make_one_partition_rbound()

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

Definition at line 1405 of file partbounds.c.

References Assert, castNode, Const::constisnull, Const::constvalue, PartitionRangeBound::datums, elog, ERROR, i, PartitionHashBound::index, 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 create_range_bounds().

1406 {
1407  PartitionRangeBound *bound;
1408  ListCell *lc;
1409  int i;
1410 
1411  Assert(datums != NIL);
1412 
1413  bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
1414  bound->index = index;
1415  bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
1416  bound->kind = (PartitionRangeDatumKind *) palloc0(key->partnatts *
1417  sizeof(PartitionRangeDatumKind));
1418  bound->lower = lower;
1419 
1420  i = 0;
1421  foreach(lc, datums)
1422  {
1424 
1425  /* What's contained in this range datum? */
1426  bound->kind[i] = datum->kind;
1427 
1428  if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
1429  {
1430  Const *val = castNode(Const, datum->value);
1431 
1432  if (val->constisnull)
1433  elog(ERROR, "invalid range bound datum");
1434  bound->datums[i] = val->constvalue;
1435  }
1436 
1437  i++;
1438  }
1439 
1440  return bound;
1441 }
Datum constvalue
Definition: primnodes.h:200
#define NIL
Definition: pg_list.h:65
PartitionRangeDatumKind * kind
Definition: partbounds.c:68
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:43
#define castNode(_type_, nodeptr)
Definition: nodes.h:594
PartitionRangeDatumKind
Definition: parsenodes.h:834
PartitionRangeDatumKind kind
Definition: parsenodes.h:845
Definition: type.h:89
#define ERROR
Definition: elog.h:43
void * palloc0(Size size)
Definition: mcxt.c:980
uintptr_t Datum
Definition: postgres.h:367
#define Assert(condition)
Definition: c.h:739
#define lfirst(lc)
Definition: pg_list.h:190
#define elog(elevel,...)
Definition: elog.h:228
int i
long val
Definition: informix.c:664
bool constisnull
Definition: primnodes.h:201

◆ make_partition_op_expr()

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

Definition at line 1878 of file partbounds.c.

References ScalarArrayOpExpr::args, ArrayExpr::array_collid, ArrayExpr::array_typeid, Assert, COERCE_EXPLICIT_CAST, ArrayExpr::element_typeid, ArrayExpr::elements, elog, ERROR, get_array_type(), get_opcode(), get_partition_operator(), ScalarArrayOpExpr::inputcollid, InvalidOid, IsA, lappend(), lfirst, linitial, list_length(), list_make2, ScalarArrayOpExpr::location, ArrayExpr::location, make_opclause(), makeBoolExpr(), makeNode, makeRelabelType(), ArrayExpr::multidims, NIL, ScalarArrayOpExpr::opfuncid, ScalarArrayOpExpr::opno, OR_EXPR, PartitionKeyData::partcollation, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionKeyData::partopcintype, PartitionKeyData::parttypcoll, PartitionKeyData::parttypid, PartitionKeyData::strategy, type_is_array, and ScalarArrayOpExpr::useOr.

Referenced by get_qual_for_list(), and get_qual_for_range().

1880 {
1881  Oid operoid;
1882  bool need_relabel = false;
1883  Expr *result = NULL;
1884 
1885  /* Get the correct btree operator for this partitioning column */
1886  operoid = get_partition_operator(key, keynum, strategy, &need_relabel);
1887 
1888  /*
1889  * Chosen operator may be such that the non-Const operand needs to be
1890  * coerced, so apply the same; see the comment in
1891  * get_partition_operator().
1892  */
1893  if (!IsA(arg1, Const) &&
1894  (need_relabel ||
1895  key->partcollation[keynum] != key->parttypcoll[keynum]))
1896  arg1 = (Expr *) makeRelabelType(arg1,
1897  key->partopcintype[keynum],
1898  -1,
1899  key->partcollation[keynum],
1901 
1902  /* Generate the actual expression */
1903  switch (key->strategy)
1904  {
1906  {
1907  List *elems = (List *) arg2;
1908  int nelems = list_length(elems);
1909 
1910  Assert(nelems >= 1);
1911  Assert(keynum == 0);
1912 
1913  if (nelems > 1 &&
1914  !type_is_array(key->parttypid[keynum]))
1915  {
1916  ArrayExpr *arrexpr;
1917  ScalarArrayOpExpr *saopexpr;
1918 
1919  /* Construct an ArrayExpr for the right-hand inputs */
1920  arrexpr = makeNode(ArrayExpr);
1921  arrexpr->array_typeid =
1922  get_array_type(key->parttypid[keynum]);
1923  arrexpr->array_collid = key->parttypcoll[keynum];
1924  arrexpr->element_typeid = key->parttypid[keynum];
1925  arrexpr->elements = elems;
1926  arrexpr->multidims = false;
1927  arrexpr->location = -1;
1928 
1929  /* Build leftop = ANY (rightop) */
1930  saopexpr = makeNode(ScalarArrayOpExpr);
1931  saopexpr->opno = operoid;
1932  saopexpr->opfuncid = get_opcode(operoid);
1933  saopexpr->useOr = true;
1934  saopexpr->inputcollid = key->partcollation[keynum];
1935  saopexpr->args = list_make2(arg1, arrexpr);
1936  saopexpr->location = -1;
1937 
1938  result = (Expr *) saopexpr;
1939  }
1940  else
1941  {
1942  List *elemops = NIL;
1943  ListCell *lc;
1944 
1945  foreach(lc, elems)
1946  {
1947  Expr *elem = lfirst(lc),
1948  *elemop;
1949 
1950  elemop = make_opclause(operoid,
1951  BOOLOID,
1952  false,
1953  arg1, elem,
1954  InvalidOid,
1955  key->partcollation[keynum]);
1956  elemops = lappend(elemops, elemop);
1957  }
1958 
1959  result = nelems > 1 ? makeBoolExpr(OR_EXPR, elemops, -1) : linitial(elemops);
1960  }
1961  break;
1962  }
1963 
1965  result = make_opclause(operoid,
1966  BOOLOID,
1967  false,
1968  arg1, arg2,
1969  InvalidOid,
1970  key->partcollation[keynum]);
1971  break;
1972 
1973  default:
1974  elog(ERROR, "invalid partitioning strategy");
1975  break;
1976  }
1977 
1978  return result;
1979 }
#define list_make2(x1, x2)
Definition: pg_list.h:229
bool multidims
Definition: primnodes.h:978
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
Oid get_array_type(Oid typid)
Definition: lsyscache.c:2554
Oid array_typeid
Definition: primnodes.h:974
unsigned int Oid
Definition: postgres_ext.h:31
Expr * make_opclause(Oid opno, Oid opresulttype, bool opretset, Expr *leftop, Expr *rightop, Oid opcollid, Oid inputcollid)
Definition: makefuncs.c:607
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:367
Oid * parttypcoll
Definition: partcache.h:46
#define linitial(l)
Definition: pg_list.h:195
#define ERROR
Definition: elog.h:43
RelabelType * makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod, Oid rcollid, CoercionForm rformat)
Definition: makefuncs.c:400
List * elements
Definition: primnodes.h:977
List * lappend(List *list, void *datum)
Definition: list.c:322
Oid * partcollation
Definition: partcache.h:38
int location
Definition: primnodes.h:979
static Oid get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel)
Definition: partbounds.c:1842
#define InvalidOid
Definition: postgres_ext.h:36
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1092
#define makeNode(_type_)
Definition: nodes.h:573
#define Assert(condition)
Definition: c.h:739
#define lfirst(lc)
Definition: pg_list.h:190
Oid array_collid
Definition: primnodes.h:975
static int list_length(const List *l)
Definition: pg_list.h:169
#define type_is_array(typid)
Definition: lsyscache.h:184
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:799
Oid element_typeid
Definition: primnodes.h:976
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:800
Oid * partopcintype
Definition: partcache.h:34
#define elog(elevel,...)
Definition: elog.h:228
Definition: pg_list.h:50

◆ partition_bounds_copy()

PartitionBoundInfo partition_bounds_copy ( PartitionBoundInfo  src,
PartitionKey  key 
)

Definition at line 780 of file partbounds.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 RelationBuildPartitionDesc().

782 {
784  int i;
785  int ndatums;
786  int partnatts;
787  int num_indexes;
788 
790 
791  dest->strategy = src->strategy;
792  ndatums = dest->ndatums = src->ndatums;
793  partnatts = key->partnatts;
794 
795  num_indexes = get_partition_bound_num_indexes(src);
796 
797  /* List partitioned tables have only a single partition key. */
798  Assert(key->strategy != PARTITION_STRATEGY_LIST || partnatts == 1);
799 
800  dest->datums = (Datum **) palloc(sizeof(Datum *) * ndatums);
801 
802  if (src->kind != NULL)
803  {
804  dest->kind = (PartitionRangeDatumKind **) palloc(ndatums *
805  sizeof(PartitionRangeDatumKind *));
806  for (i = 0; i < ndatums; i++)
807  {
808  dest->kind[i] = (PartitionRangeDatumKind *) palloc(partnatts *
809  sizeof(PartitionRangeDatumKind));
810 
811  memcpy(dest->kind[i], src->kind[i],
812  sizeof(PartitionRangeDatumKind) * key->partnatts);
813  }
814  }
815  else
816  dest->kind = NULL;
817 
818  for (i = 0; i < ndatums; i++)
819  {
820  int j;
821 
822  /*
823  * For a corresponding to hash partition, datums array will have two
824  * elements - modulus and remainder.
825  */
826  bool hash_part = (key->strategy == PARTITION_STRATEGY_HASH);
827  int natts = hash_part ? 2 : partnatts;
828 
829  dest->datums[i] = (Datum *) palloc(sizeof(Datum) * natts);
830 
831  for (j = 0; j < natts; j++)
832  {
833  bool byval;
834  int typlen;
835 
836  if (hash_part)
837  {
838  typlen = sizeof(int32); /* Always int4 */
839  byval = true; /* int4 is pass-by-value */
840  }
841  else
842  {
843  byval = key->parttypbyval[j];
844  typlen = key->parttyplen[j];
845  }
846 
847  if (dest->kind == NULL ||
848  dest->kind[i][j] == PARTITION_RANGE_DATUM_VALUE)
849  dest->datums[i][j] = datumCopy(src->datums[i][j],
850  byval, typlen);
851  }
852  }
853 
854  dest->indexes = (int *) palloc(sizeof(int) * num_indexes);
855  memcpy(dest->indexes, src->indexes, sizeof(int) * num_indexes);
856 
857  dest->null_index = src->null_index;
858  dest->default_index = src->default_index;
859 
860  return dest;
861 }
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
PartitionRangeDatumKind
Definition: parsenodes.h:834
signed int int32
Definition: c.h:347
static int get_partition_bound_num_indexes(PartitionBoundInfo b)
Definition: partbounds.c:1797
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:130
uintptr_t Datum
Definition: postgres.h:367
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:798
bool * parttypbyval
Definition: partcache.h:44
#define Assert(condition)
Definition: c.h:739
int16 * parttyplen
Definition: partcache.h:43
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:799
void * palloc(Size size)
Definition: mcxt.c:949
int i
struct PartitionBoundInfoData * PartitionBoundInfo
Definition: partdefs.h:16

◆ partition_bounds_create()

PartitionBoundInfo partition_bounds_create ( PartitionBoundSpec **  boundspecs,
int  nparts,
PartitionKey  key,
int **  mapping 
)

Definition at line 173 of file partbounds.c.

References Assert, create_hash_bounds(), create_list_bounds(), create_range_bounds(), elog, ERROR, i, palloc(), PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, and PartitionKeyData::strategy.

Referenced by RelationBuildPartitionDesc().

175 {
176  int i;
177 
178  Assert(nparts > 0);
179 
180  /*
181  * For each partitioning method, we first convert the partition bounds
182  * from their parser node representation to the internal representation,
183  * along with any additional preprocessing (such as de-duplicating range
184  * bounds). Resulting bound datums are then added to the 'datums' array
185  * in PartitionBoundInfo. For each datum added, an integer indicating the
186  * canonical partition index is added to the 'indexes' array.
187  *
188  * For each bound, we remember its partition's position (0-based) in the
189  * original list to later map it to the canonical index.
190  */
191 
192  /*
193  * Initialize mapping array with invalid values, this is filled within
194  * each sub-routine below depending on the bound type.
195  */
196  *mapping = (int *) palloc(sizeof(int) * nparts);
197  for (i = 0; i < nparts; i++)
198  (*mapping)[i] = -1;
199 
200  switch (key->strategy)
201  {
203  return create_hash_bounds(boundspecs, nparts, key, mapping);
204 
206  return create_list_bounds(boundspecs, nparts, key, mapping);
207 
209  return create_range_bounds(boundspecs, nparts, key, mapping);
210 
211  default:
212  elog(ERROR, "unexpected partition strategy: %d",
213  (int) key->strategy);
214  break;
215  }
216 
217  Assert(false);
218  return NULL; /* keep compiler quiet */
219 }
#define ERROR
Definition: elog.h:43
static PartitionBoundInfo create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
Definition: partbounds.c:226
static PartitionBoundInfo create_list_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
Definition: partbounds.c:308
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:798
#define Assert(condition)
Definition: c.h:739
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:799
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:800
static PartitionBoundInfo create_range_bounds(PartitionBoundSpec **boundspecs, int nparts, PartitionKey key, int **mapping)
Definition: partbounds.c:465
void * palloc(Size size)
Definition: mcxt.c:949
#define elog(elevel,...)
Definition: elog.h:228
int i

◆ partition_bounds_equal()

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

Definition at line 667 of file partbounds.c.

References Assert, datumIsEqual(), PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, get_hash_partition_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_partitionwise_join().

669 {
670  int i;
671 
672  if (b1->strategy != b2->strategy)
673  return false;
674 
675  if (b1->ndatums != b2->ndatums)
676  return false;
677 
678  if (b1->null_index != b2->null_index)
679  return false;
680 
681  if (b1->default_index != b2->default_index)
682  return false;
683 
685  {
686  int greatest_modulus = get_hash_partition_greatest_modulus(b1);
687 
688  /*
689  * If two hash partitioned tables have different greatest moduli,
690  * their partition schemes don't match.
691  */
692  if (greatest_modulus != get_hash_partition_greatest_modulus(b2))
693  return false;
694 
695  /*
696  * We arrange the partitions in the ascending order of their moduli
697  * and remainders. Also every modulus is factor of next larger
698  * modulus. Therefore we can safely store index of a given partition
699  * in indexes array at remainder of that partition. Also entries at
700  * (remainder + N * modulus) positions in indexes array are all same
701  * for (modulus, remainder) specification for any partition. Thus
702  * datums array from both the given bounds are same, if and only if
703  * their indexes array will be same. So, it suffices to compare
704  * indexes array.
705  */
706  for (i = 0; i < greatest_modulus; i++)
707  if (b1->indexes[i] != b2->indexes[i])
708  return false;
709 
710 #ifdef USE_ASSERT_CHECKING
711 
712  /*
713  * Nonetheless make sure that the bounds are indeed same when the
714  * indexes match. Hash partition bound stores modulus and remainder
715  * at b1->datums[i][0] and b1->datums[i][1] position respectively.
716  */
717  for (i = 0; i < b1->ndatums; i++)
718  Assert((b1->datums[i][0] == b2->datums[i][0] &&
719  b1->datums[i][1] == b2->datums[i][1]));
720 #endif
721  }
722  else
723  {
724  for (i = 0; i < b1->ndatums; i++)
725  {
726  int j;
727 
728  for (j = 0; j < partnatts; j++)
729  {
730  /* For range partitions, the bounds might not be finite. */
731  if (b1->kind != NULL)
732  {
733  /* The different kinds of bound all differ from each other */
734  if (b1->kind[i][j] != b2->kind[i][j])
735  return false;
736 
737  /*
738  * Non-finite bounds are equal without further
739  * examination.
740  */
741  if (b1->kind[i][j] != PARTITION_RANGE_DATUM_VALUE)
742  continue;
743  }
744 
745  /*
746  * Compare the actual values. Note that it would be both
747  * incorrect and unsafe to invoke the comparison operator
748  * derived from the partitioning specification here. It would
749  * be incorrect because we want the relcache entry to be
750  * updated for ANY change to the partition bounds, not just
751  * those that the partitioning operator thinks are
752  * significant. It would be unsafe because we might reach
753  * this code in the context of an aborted transaction, and an
754  * arbitrary partitioning operator might not be safe in that
755  * context. datumIsEqual() should be simple enough to be
756  * safe.
757  */
758  if (!datumIsEqual(b1->datums[i][j], b2->datums[i][j],
759  parttypbyval[j], parttyplen[j]))
760  return false;
761  }
762 
763  if (b1->indexes[i] != b2->indexes[i])
764  return false;
765  }
766 
767  /* There are ndatums+1 indexes in case of range partitions */
768  if (b1->strategy == PARTITION_STRATEGY_RANGE &&
769  b1->indexes[i] != b2->indexes[i])
770  return false;
771  }
772  return true;
773 }
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
bool datumIsEqual(Datum value1, Datum value2, bool typByVal, int typLen)
Definition: datum.c:221
int get_hash_partition_greatest_modulus(PartitionBoundInfo bound)
Definition: partbounds.c:1388
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:798
#define Assert(condition)
Definition: c.h:739
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:800
int i

◆ partition_hash_bsearch()

int partition_hash_bsearch ( PartitionBoundInfo  boundinfo,
int  modulus,
int  remainder 
)

Definition at line 1708 of file partbounds.c.

References DatumGetInt32, PartitionBoundInfoData::datums, PartitionBoundInfoData::ndatums, and partition_hbound_cmp().

Referenced by check_new_partition_bound().

1710 {
1711  int lo,
1712  hi,
1713  mid;
1714 
1715  lo = -1;
1716  hi = boundinfo->ndatums - 1;
1717  while (lo < hi)
1718  {
1719  int32 cmpval,
1720  bound_modulus,
1721  bound_remainder;
1722 
1723  mid = (lo + hi + 1) / 2;
1724  bound_modulus = DatumGetInt32(boundinfo->datums[mid][0]);
1725  bound_remainder = DatumGetInt32(boundinfo->datums[mid][1]);
1726  cmpval = partition_hbound_cmp(bound_modulus, bound_remainder,
1727  modulus, remainder);
1728  if (cmpval <= 0)
1729  {
1730  lo = mid;
1731 
1732  if (cmpval == 0)
1733  break;
1734  }
1735  else
1736  hi = mid - 1;
1737  }
1738 
1739  return lo;
1740 }
#define DatumGetInt32(X)
Definition: postgres.h:472
signed int int32
Definition: c.h:347
static int32 partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
Definition: partbounds.c:1556

◆ partition_hbound_cmp()

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

Definition at line 1556 of file partbounds.c.

Referenced by partition_hash_bsearch(), and qsort_partition_hbound_cmp().

1557 {
1558  if (modulus1 < modulus2)
1559  return -1;
1560  if (modulus1 > modulus2)
1561  return 1;
1562  if (modulus1 == modulus2 && remainder1 != remainder2)
1563  return (remainder1 > remainder2) ? 1 : -1;
1564  return 0;
1565 }

◆ partition_list_bsearch()

int partition_list_bsearch ( FmgrInfo partsupfunc,
Oid partcollation,
PartitionBoundInfo  boundinfo,
Datum  value,
bool is_equal 
)

Definition at line 1576 of file partbounds.c.

References DatumGetInt32, PartitionBoundInfoData::datums, FunctionCall2Coll(), and PartitionBoundInfoData::ndatums.

Referenced by check_new_partition_bound(), get_matching_list_bounds(), and get_partition_for_tuple().

1579 {
1580  int lo,
1581  hi,
1582  mid;
1583 
1584  lo = -1;
1585  hi = boundinfo->ndatums - 1;
1586  while (lo < hi)
1587  {
1588  int32 cmpval;
1589 
1590  mid = (lo + hi + 1) / 2;
1591  cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
1592  partcollation[0],
1593  boundinfo->datums[mid][0],
1594  value));
1595  if (cmpval <= 0)
1596  {
1597  lo = mid;
1598  *is_equal = (cmpval == 0);
1599  if (*is_equal)
1600  break;
1601  }
1602  else
1603  hi = mid - 1;
1604  }
1605 
1606  return lo;
1607 }
#define DatumGetInt32(X)
Definition: postgres.h:472
static struct @145 value
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1150
signed int int32
Definition: c.h:347

◆ partition_range_bsearch()

static int partition_range_bsearch ( int  partnatts,
FmgrInfo partsupfunc,
Oid partcollation,
PartitionBoundInfo  boundinfo,
PartitionRangeBound probe,
bool is_equal 
)
static

Definition at line 1619 of file partbounds.c.

References PartitionBoundInfoData::datums, PartitionBoundInfoData::indexes, PartitionBoundInfoData::kind, PartitionBoundInfoData::ndatums, and partition_rbound_cmp().

Referenced by check_new_partition_bound().

1623 {
1624  int lo,
1625  hi,
1626  mid;
1627 
1628  lo = -1;
1629  hi = boundinfo->ndatums - 1;
1630  while (lo < hi)
1631  {
1632  int32 cmpval;
1633 
1634  mid = (lo + hi + 1) / 2;
1635  cmpval = partition_rbound_cmp(partnatts, partsupfunc,
1636  partcollation,
1637  boundinfo->datums[mid],
1638  boundinfo->kind[mid],
1639  (boundinfo->indexes[mid] == -1),
1640  probe);
1641  if (cmpval <= 0)
1642  {
1643  lo = mid;
1644  *is_equal = (cmpval == 0);
1645 
1646  if (*is_equal)
1647  break;
1648  }
1649  else
1650  hi = mid - 1;
1651  }
1652 
1653  return lo;
1654 }
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
signed int int32
Definition: c.h:347
static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partbounds.c:1461

◆ partition_range_datum_bsearch()

int partition_range_datum_bsearch ( FmgrInfo partsupfunc,
Oid partcollation,
PartitionBoundInfo  boundinfo,
int  nvalues,
Datum values,
bool is_equal 
)

Definition at line 1665 of file partbounds.c.

References PartitionBoundInfoData::datums, PartitionBoundInfoData::kind, PartitionBoundInfoData::ndatums, and partition_rbound_datum_cmp().

Referenced by get_matching_range_bounds(), and get_partition_for_tuple().

1668 {
1669  int lo,
1670  hi,
1671  mid;
1672 
1673  lo = -1;
1674  hi = boundinfo->ndatums - 1;
1675  while (lo < hi)
1676  {
1677  int32 cmpval;
1678 
1679  mid = (lo + hi + 1) / 2;
1680  cmpval = partition_rbound_datum_cmp(partsupfunc,
1681  partcollation,
1682  boundinfo->datums[mid],
1683  boundinfo->kind[mid],
1684  values,
1685  nvalues);
1686  if (cmpval <= 0)
1687  {
1688  lo = mid;
1689  *is_equal = (cmpval == 0);
1690 
1691  if (*is_equal)
1692  break;
1693  }
1694  else
1695  hi = mid - 1;
1696  }
1697 
1698  return lo;
1699 }
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
signed int int32
Definition: c.h:347
int32 partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *tuple_datums, int n_tuple_datums)
Definition: partbounds.c:1525
static Datum values[MAXATTR]
Definition: bootstrap.c:167

◆ partition_rbound_cmp()

static int32 partition_rbound_cmp ( int  partnatts,
FmgrInfo partsupfunc,
Oid partcollation,
Datum datums1,
PartitionRangeDatumKind kind1,
bool  lower1,
PartitionRangeBound b2 
)
static

Definition at line 1461 of file partbounds.c.

References DatumGetInt32, PartitionRangeBound::datums, FunctionCall2Coll(), i, PartitionRangeBound::kind, PartitionRangeBound::lower, and PARTITION_RANGE_DATUM_VALUE.

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

1465 {
1466  int32 cmpval = 0; /* placate compiler */
1467  int i;
1468  Datum *datums2 = b2->datums;
1469  PartitionRangeDatumKind *kind2 = b2->kind;
1470  bool lower2 = b2->lower;
1471 
1472  for (i = 0; i < partnatts; i++)
1473  {
1474  /*
1475  * First, handle cases where the column is unbounded, which should not
1476  * invoke the comparison procedure, and should not consider any later
1477  * columns. Note that the PartitionRangeDatumKind enum elements
1478  * compare the same way as the values they represent.
1479  */
1480  if (kind1[i] < kind2[i])
1481  return -1;
1482  else if (kind1[i] > kind2[i])
1483  return 1;
1484  else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
1485 
1486  /*
1487  * The column bounds are both MINVALUE or both MAXVALUE. No later
1488  * columns should be considered, but we still need to compare
1489  * whether they are upper or lower bounds.
1490  */
1491  break;
1492 
1493  cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
1494  partcollation[i],
1495  datums1[i],
1496  datums2[i]));
1497  if (cmpval != 0)
1498  break;
1499  }
1500 
1501  /*
1502  * If the comparison is anything other than equal, we're done. If they
1503  * compare equal though, we still have to consider whether the boundaries
1504  * are inclusive or exclusive. Exclusive one is considered smaller of the
1505  * two.
1506  */
1507  if (cmpval == 0 && lower1 != lower2)
1508  cmpval = lower1 ? 1 : -1;
1509 
1510  return cmpval;
1511 }
PartitionRangeDatumKind * kind
Definition: partbounds.c:68
#define DatumGetInt32(X)
Definition: postgres.h:472
PartitionRangeDatumKind
Definition: parsenodes.h:834
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1150
signed int int32
Definition: c.h:347
uintptr_t Datum
Definition: postgres.h:367
int i

◆ partition_rbound_datum_cmp()

int32 partition_rbound_datum_cmp ( FmgrInfo partsupfunc,
Oid partcollation,
Datum rb_datums,
PartitionRangeDatumKind rb_kind,
Datum tuple_datums,
int  n_tuple_datums 
)

Definition at line 1525 of file partbounds.c.

References DatumGetInt32, FunctionCall2Coll(), i, PARTITION_RANGE_DATUM_MAXVALUE, and PARTITION_RANGE_DATUM_MINVALUE.

Referenced by get_matching_range_bounds(), and partition_range_datum_bsearch().

1528 {
1529  int i;
1530  int32 cmpval = -1;
1531 
1532  for (i = 0; i < n_tuple_datums; i++)
1533  {
1534  if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
1535  return -1;
1536  else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
1537  return 1;
1538 
1539  cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
1540  partcollation[i],
1541  rb_datums[i],
1542  tuple_datums[i]));
1543  if (cmpval != 0)
1544  break;
1545  }
1546 
1547  return cmpval;
1548 }
#define DatumGetInt32(X)
Definition: postgres.h:472
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1150
signed int int32
Definition: c.h:347
int i

◆ partitions_are_ordered()

bool partitions_are_ordered ( PartitionBoundInfo  boundinfo,
int  nparts 
)

Definition at line 875 of file partbounds.c.

References Assert, PartitionBoundInfoData::ndatums, partition_bound_accepts_nulls, partition_bound_has_default, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, and PartitionBoundInfoData::strategy.

Referenced by build_partition_pathkeys(), and generate_orderedappend_paths().

876 {
877  Assert(boundinfo != NULL);
878 
879  switch (boundinfo->strategy)
880  {
882 
883  /*
884  * RANGE-type partitioning guarantees that the partitions can be
885  * scanned in the order that they're defined in the PartitionDesc
886  * to provide sequential, non-overlapping ranges of tuples.
887  * However, if a DEFAULT partition exists then it doesn't work, as
888  * that could contain tuples from either below or above the
889  * defined range, or tuples belonging to gaps between partitions.
890  */
891  if (!partition_bound_has_default(boundinfo))
892  return true;
893  break;
894 
896 
897  /*
898  * LIST partitioning can also guarantee ordering, but only if the
899  * partitions don't accept interleaved values. We could likely
900  * check for this by looping over the PartitionBound's indexes
901  * array to check that the indexes are in order. For now, let's
902  * just keep it simple and just accept LIST partitioning when
903  * there's no DEFAULT partition, exactly one value per partition,
904  * and optionally a NULL partition that does not accept any other
905  * values. Such a NULL partition will come last in the
906  * PartitionDesc, and the other partitions will be properly
907  * ordered. This is a cheap test to make as it does not require
908  * any per-partition processing. Maybe we'd like to handle more
909  * complex cases in the future.
910  */
911  if (partition_bound_has_default(boundinfo))
912  return false;
913 
914  if (boundinfo->ndatums + partition_bound_accepts_nulls(boundinfo)
915  == nparts)
916  return true;
917  break;
918 
919  default:
920  /* HASH, or some other strategy */
921  break;
922  }
923 
924  return false;
925 }
#define partition_bound_has_default(bi)
Definition: partbounds.h:75
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:74
#define Assert(condition)
Definition: c.h:739
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:799
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:800

◆ qsort_partition_hbound_cmp()

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

Definition at line 1748 of file partbounds.c.

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

Referenced by create_hash_bounds().

1749 {
1750  PartitionHashBound *h1 = (*(PartitionHashBound *const *) a);
1751  PartitionHashBound *h2 = (*(PartitionHashBound *const *) b);
1752 
1753  return partition_hbound_cmp(h1->modulus, h1->remainder,
1754  h2->modulus, h2->remainder);
1755 }
static int32 partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
Definition: partbounds.c:1556

◆ qsort_partition_list_value_cmp()

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

Definition at line 1763 of file partbounds.c.

References DatumGetInt32, FunctionCall2Coll(), sort-test::key, PartitionKeyData::partcollation, and PartitionKeyData::partsupfunc.

Referenced by create_list_bounds().

1764 {
1765  Datum val1 = (*(PartitionListValue *const *) a)->value,
1766  val2 = (*(PartitionListValue *const *) b)->value;
1768 
1770  key->partcollation[0],
1771  val1, val2));
1772 }
#define DatumGetInt32(X)
Definition: postgres.h:472
FmgrInfo * partsupfunc
Definition: partcache.h:35
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1150
Oid * partcollation
Definition: partcache.h:38
uintptr_t Datum
Definition: postgres.h:367
void * arg
struct PartitionKeyData * PartitionKey
Definition: partdefs.h:18

◆ qsort_partition_rbound_cmp()

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

Definition at line 1780 of file partbounds.c.

References PartitionRangeBound::datums, sort-test::key, PartitionRangeBound::kind, PartitionRangeBound::lower, PartitionKeyData::partcollation, partition_rbound_cmp(), PartitionKeyData::partnatts, and PartitionKeyData::partsupfunc.

Referenced by create_range_bounds().

1781 {
1782  PartitionRangeBound *b1 = (*(PartitionRangeBound *const *) a);
1783  PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
1785 
1786  return partition_rbound_cmp(key->partnatts, key->partsupfunc,
1787  key->partcollation, b1->datums, b1->kind,
1788  b1->lower, b2);
1789 }
PartitionRangeDatumKind * kind
Definition: partbounds.c:68
FmgrInfo * partsupfunc
Definition: partcache.h:35
Oid * partcollation
Definition: partcache.h:38
static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2)
Definition: partbounds.c:1461
void * arg
struct PartitionKeyData * PartitionKey
Definition: partdefs.h:18

◆ satisfies_hash_partition()

Datum satisfies_hash_partition ( PG_FUNCTION_ARGS  )

Definition at line 2786 of file partbounds.c.

References AccessShareLock, ARR_ELEMTYPE, DatumGetUInt64, deconstruct_array(), ereport, errcode(), errmsg(), ERROR, fmgr_info_copy(), FmgrInfo::fn_mcxt, format_type_be(), FunctionCall2Coll(), get_fn_expr_argtype(), get_fn_expr_variadic(), get_rel_name(), get_typlenbyvalalign(), hash(), hash_combine64(), HASH_PARTITION_SEED, i, IsBinaryCoercible(), sort-test::key, MemoryContextAllocZero(), PartitionHashBound::modulus, NoLock, offsetof, OidIsValid, PartitionKeyData::partcollation, 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, PartitionHashBound::remainder, PartitionKeyData::strategy, try_relation_open(), and UInt64GetDatum.

2787 {
2788  typedef struct ColumnsHashData
2789  {
2790  Oid relid;
2791  int nkeys;
2792  Oid variadic_type;
2793  int16 variadic_typlen;
2794  bool variadic_typbyval;
2795  char variadic_typalign;
2796  Oid partcollid[PARTITION_MAX_KEYS];
2797  FmgrInfo partsupfunc[FLEXIBLE_ARRAY_MEMBER];
2798  } ColumnsHashData;
2799  Oid parentId;
2800  int modulus;
2801  int remainder;
2803  ColumnsHashData *my_extra;
2804  uint64 rowHash = 0;
2805 
2806  /* Return null if the parent OID, modulus, or remainder is NULL. */
2807  if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2))
2808  PG_RETURN_NULL();
2809  parentId = PG_GETARG_OID(0);
2810  modulus = PG_GETARG_INT32(1);
2811  remainder = PG_GETARG_INT32(2);
2812 
2813  /* Sanity check modulus and remainder. */
2814  if (modulus <= 0)
2815  ereport(ERROR,
2816  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2817  errmsg("modulus for hash partition must be a positive integer")));
2818  if (remainder < 0)
2819  ereport(ERROR,
2820  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2821  errmsg("remainder for hash partition must be a non-negative integer")));
2822  if (remainder >= modulus)
2823  ereport(ERROR,
2824  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2825  errmsg("remainder for hash partition must be less than modulus")));
2826 
2827  /*
2828  * Cache hash function information.
2829  */
2830  my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
2831  if (my_extra == NULL || my_extra->relid != parentId)
2832  {
2833  Relation parent;
2834  PartitionKey key;
2835  int j;
2836 
2837  /* Open parent relation and fetch partition key info */
2838  parent = try_relation_open(parentId, AccessShareLock);
2839  if (parent == NULL)
2840  PG_RETURN_NULL();
2841  key = RelationGetPartitionKey(parent);
2842 
2843  /* Reject parent table that is not hash-partitioned. */
2844  if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE ||
2846  ereport(ERROR,
2847  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2848  errmsg("\"%s\" is not a hash partitioned table",
2849  get_rel_name(parentId))));
2850 
2851  if (!get_fn_expr_variadic(fcinfo->flinfo))
2852  {
2853  int nargs = PG_NARGS() - 3;
2854 
2855  /* complain if wrong number of column values */
2856  if (key->partnatts != nargs)
2857  ereport(ERROR,
2858  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2859  errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
2860  key->partnatts, nargs)));
2861 
2862  /* allocate space for our cache */
2863  fcinfo->flinfo->fn_extra =
2864  MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
2865  offsetof(ColumnsHashData, partsupfunc) +
2866  sizeof(FmgrInfo) * nargs);
2867  my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
2868  my_extra->relid = parentId;
2869  my_extra->nkeys = key->partnatts;
2870  memcpy(my_extra->partcollid, key->partcollation,
2871  key->partnatts * sizeof(Oid));
2872 
2873  /* check argument types and save fmgr_infos */
2874  for (j = 0; j < key->partnatts; ++j)
2875  {
2876  Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, j + 3);
2877 
2878  if (argtype != key->parttypid[j] && !IsBinaryCoercible(argtype, key->parttypid[j]))
2879  ereport(ERROR,
2880  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2881  errmsg("column %d of the partition key has type \"%s\", but supplied value is of type \"%s\"",
2882  j + 1, format_type_be(key->parttypid[j]), format_type_be(argtype))));
2883 
2884  fmgr_info_copy(&my_extra->partsupfunc[j],
2885  &key->partsupfunc[j],
2886  fcinfo->flinfo->fn_mcxt);
2887  }
2888  }
2889  else
2890  {
2891  ArrayType *variadic_array = PG_GETARG_ARRAYTYPE_P(3);
2892 
2893  /* allocate space for our cache -- just one FmgrInfo in this case */
2894  fcinfo->flinfo->fn_extra =
2895  MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
2896  offsetof(ColumnsHashData, partsupfunc) +
2897  sizeof(FmgrInfo));
2898  my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
2899  my_extra->relid = parentId;
2900  my_extra->nkeys = key->partnatts;
2901  my_extra->variadic_type = ARR_ELEMTYPE(variadic_array);
2902  get_typlenbyvalalign(my_extra->variadic_type,
2903  &my_extra->variadic_typlen,
2904  &my_extra->variadic_typbyval,
2905  &my_extra->variadic_typalign);
2906  my_extra->partcollid[0] = key->partcollation[0];
2907 
2908  /* check argument types */
2909  for (j = 0; j < key->partnatts; ++j)
2910  if (key->parttypid[j] != my_extra->variadic_type)
2911  ereport(ERROR,
2912  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2913  errmsg("column %d of the partition key has type \"%s\", but supplied value is of type \"%s\"",
2914  j + 1,
2915  format_type_be(key->parttypid[j]),
2916  format_type_be(my_extra->variadic_type))));
2917 
2918  fmgr_info_copy(&my_extra->partsupfunc[0],
2919  &key->partsupfunc[0],
2920  fcinfo->flinfo->fn_mcxt);
2921  }
2922 
2923  /* Hold lock until commit */
2924  relation_close(parent, NoLock);
2925  }
2926 
2927  if (!OidIsValid(my_extra->variadic_type))
2928  {
2929  int nkeys = my_extra->nkeys;
2930  int i;
2931 
2932  /*
2933  * For a non-variadic call, neither the number of arguments nor their
2934  * types can change across calls, so avoid the expense of rechecking
2935  * here.
2936  */
2937 
2938  for (i = 0; i < nkeys; i++)
2939  {
2940  Datum hash;
2941 
2942  /* keys start from fourth argument of function. */
2943  int argno = i + 3;
2944 
2945  if (PG_ARGISNULL(argno))
2946  continue;
2947 
2948  hash = FunctionCall2Coll(&my_extra->partsupfunc[i],
2949  my_extra->partcollid[i],
2950  PG_GETARG_DATUM(argno),
2951  seed);
2952 
2953  /* Form a single 64-bit hash value */
2954  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
2955  }
2956  }
2957  else
2958  {
2959  ArrayType *variadic_array = PG_GETARG_ARRAYTYPE_P(3);
2960  int i;
2961  int nelems;
2962  Datum *datum;
2963  bool *isnull;
2964 
2965  deconstruct_array(variadic_array,
2966  my_extra->variadic_type,
2967  my_extra->variadic_typlen,
2968  my_extra->variadic_typbyval,
2969  my_extra->variadic_typalign,
2970  &datum, &isnull, &nelems);
2971 
2972  /* complain if wrong number of column values */
2973  if (nelems != my_extra->nkeys)
2974  ereport(ERROR,
2975  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2976  errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
2977  my_extra->nkeys, nelems)));
2978 
2979  for (i = 0; i < nelems; i++)
2980  {
2981  Datum hash;
2982 
2983  if (isnull[i])
2984  continue;
2985 
2986  hash = FunctionCall2Coll(&my_extra->partsupfunc[0],
2987  my_extra->partcollid[0],
2988  datum[i],
2989  seed);
2990 
2991  /* Form a single 64-bit hash value */
2992  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
2993  }
2994  }
2995 
2996  PG_RETURN_BOOL(rowHash % modulus == remainder);
2997 }
signed short int16
Definition: c.h:346
#define PG_GETARG_INT32(n)
Definition: fmgr.h:264
Definition: fmgr.h:56
MemoryContext fn_mcxt
Definition: fmgr.h:65
FmgrInfo * partsupfunc
Definition: partcache.h:35
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition: lsyscache.c:2049
#define PG_GETARG_DATUM(n)
Definition: fmgr.h:263
#define UInt64GetDatum(X)
Definition: postgres.h:648
#define AccessShareLock
Definition: lockdefs.h:36
bool get_fn_expr_variadic(FmgrInfo *flinfo)
Definition: fmgr.c:1936
int errcode(int sqlerrcode)
Definition: elog.c:608
#define PARTITION_MAX_KEYS
char * format_type_be(Oid type_oid)
Definition: format_type.c:326
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1150
Form_pg_class rd_rel
Definition: rel.h:83
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:645
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:89
#define PG_GETARG_ARRAYTYPE_P(n)
Definition: array.h:251
#define ERROR
Definition: elog.h:43
Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)
Definition: fmgr.c:1802
void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt)
Definition: fmgr.c:610
#define NoLock
Definition: lockdefs.h:34
#define PG_GETARG_OID(n)
Definition: fmgr.h:270
#define ereport(elevel, rest)
Definition: elog.h:141
bool IsBinaryCoercible(Oid srctype, Oid targettype)
static uint64 hash_combine64(uint64 a, uint64 b)
Definition: hashutils.h:48
Oid * partcollation
Definition: partcache.h:38
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:349
uintptr_t Datum
Definition: postgres.h:367
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:798
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:839
#define DatumGetUInt64(X)
Definition: postgres.h:634
#define PG_ARGISNULL(n)
Definition: fmgr.h:204
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
#define PG_NARGS()
Definition: fmgr.h:198
#define HASH_PARTITION_SEED
Definition: partition.h:20
#define RelationGetPartitionKey(relation)
Definition: rel.h:603
void deconstruct_array(ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3461
int errmsg(const char *fmt,...)
Definition: elog.c:822
int i
static unsigned hash(unsigned *uv, int n)
Definition: rege_dfa.c:541
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1730
#define ARR_ELEMTYPE(a)
Definition: array.h:280
#define PG_RETURN_NULL()
Definition: fmgr.h:335
#define offsetof(type, field)
Definition: c.h:662