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 "common/hashfn.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/lsyscache.h"
#include "utils/partcache.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 1232 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().

1234 {
1235  List *new_part_constraints;
1236  List *def_part_constraints;
1237  List *all_parts;
1238  ListCell *lc;
1239 
1240  new_part_constraints = (new_spec->strategy == PARTITION_STRATEGY_LIST)
1241  ? get_qual_for_list(parent, new_spec)
1242  : get_qual_for_range(parent, new_spec, false);
1243  def_part_constraints =
1244  get_proposed_default_constraint(new_part_constraints);
1245 
1246  /*
1247  * Map the Vars in the constraint expression from parent's attnos to
1248  * default_rel's.
1249  */
1250  def_part_constraints =
1251  map_partition_varattnos(def_part_constraints, 1, default_rel,
1252  parent);
1253 
1254  /*
1255  * If the existing constraints on the default partition imply that it will
1256  * not contain any row that would belong to the new partition, we can
1257  * avoid scanning the default partition.
1258  */
1259  if (PartConstraintImpliedByRelConstraint(default_rel, def_part_constraints))
1260  {
1261  ereport(DEBUG1,
1262  (errmsg("updated partition constraint for default partition \"%s\" is implied by existing constraints",
1263  RelationGetRelationName(default_rel))));
1264  return;
1265  }
1266 
1267  /*
1268  * Scan the default partition and its subpartitions, and check for rows
1269  * that do not satisfy the revised partition constraints.
1270  */
1271  if (default_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1272  all_parts = find_all_inheritors(RelationGetRelid(default_rel),
1273  AccessExclusiveLock, NULL);
1274  else
1275  all_parts = list_make1_oid(RelationGetRelid(default_rel));
1276 
1277  foreach(lc, all_parts)
1278  {
1279  Oid part_relid = lfirst_oid(lc);
1280  Relation part_rel;
1281  Expr *partition_constraint;
1282  EState *estate;
1283  ExprState *partqualstate = NULL;
1284  Snapshot snapshot;
1285  ExprContext *econtext;
1286  TableScanDesc scan;
1287  MemoryContext oldCxt;
1288  TupleTableSlot *tupslot;
1289 
1290  /* Lock already taken above. */
1291  if (part_relid != RelationGetRelid(default_rel))
1292  {
1293  part_rel = table_open(part_relid, NoLock);
1294 
1295  /*
1296  * Map the Vars in the constraint expression from default_rel's
1297  * the sub-partition's.
1298  */
1299  partition_constraint = make_ands_explicit(def_part_constraints);
1300  partition_constraint = (Expr *)
1301  map_partition_varattnos((List *) partition_constraint, 1,
1302  part_rel, default_rel);
1303 
1304  /*
1305  * If the partition constraints on default partition child imply
1306  * that it will not contain any row that would belong to the new
1307  * partition, we can avoid scanning the child table.
1308  */
1310  def_part_constraints))
1311  {
1312  ereport(DEBUG1,
1313  (errmsg("updated partition constraint for default partition \"%s\" is implied by existing constraints",
1314  RelationGetRelationName(part_rel))));
1315 
1316  table_close(part_rel, NoLock);
1317  continue;
1318  }
1319  }
1320  else
1321  {
1322  part_rel = default_rel;
1323  partition_constraint = make_ands_explicit(def_part_constraints);
1324  }
1325 
1326  /*
1327  * Only RELKIND_RELATION relations (i.e. leaf partitions) need to be
1328  * scanned.
1329  */
1330  if (part_rel->rd_rel->relkind != RELKIND_RELATION)
1331  {
1332  if (part_rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1333  ereport(WARNING,
1334  (errcode(ERRCODE_CHECK_VIOLATION),
1335  errmsg("skipped scanning foreign table \"%s\" which is a partition of default partition \"%s\"",
1336  RelationGetRelationName(part_rel),
1337  RelationGetRelationName(default_rel))));
1338 
1339  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
1340  table_close(part_rel, NoLock);
1341 
1342  continue;
1343  }
1344 
1345  estate = CreateExecutorState();
1346 
1347  /* Build expression execution states for partition check quals */
1348  partqualstate = ExecPrepareExpr(partition_constraint, estate);
1349 
1350  econtext = GetPerTupleExprContext(estate);
1351  snapshot = RegisterSnapshot(GetLatestSnapshot());
1352  tupslot = table_slot_create(part_rel, &estate->es_tupleTable);
1353  scan = table_beginscan(part_rel, snapshot, 0, NULL);
1354 
1355  /*
1356  * Switch to per-tuple memory context and reset it for each tuple
1357  * produced, so we don't leak memory.
1358  */
1360 
1361  while (table_scan_getnextslot(scan, ForwardScanDirection, tupslot))
1362  {
1363  econtext->ecxt_scantuple = tupslot;
1364 
1365  if (!ExecCheck(partqualstate, econtext))
1366  ereport(ERROR,
1367  (errcode(ERRCODE_CHECK_VIOLATION),
1368  errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
1369  RelationGetRelationName(default_rel))));
1370 
1371  ResetExprContext(econtext);
1373  }
1374 
1375  MemoryContextSwitchTo(oldCxt);
1376  table_endscan(scan);
1377  UnregisterSnapshot(snapshot);
1379  FreeExecutorState(estate);
1380 
1381  if (RelationGetRelid(default_rel) != RelationGetRelid(part_rel))
1382  table_close(part_rel, NoLock); /* keep the lock until commit */
1383  }
1384 }
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:904
Form_pg_class rd_rel
Definition: rel.h:84
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:506
#define ERROR
Definition: elog.h:43
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, struct ScanKeyData *key)
Definition: tableam.h:755
#define NoLock
Definition: lockdefs.h:34
List * get_proposed_default_constraint(List *new_part_constraints)
Definition: partition.c:345
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1224
#define RelationGetRelationName(relation)
Definition: rel.h:462
#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:555
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:2289
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:224
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:799
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:381
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:511
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:863
#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
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition: partition.c:198
#define RelationGetRelid(relation)
Definition: rel.h:428
static List * get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:2080
#define ResetExprContext(econtext)
Definition: executor.h:500
#define lfirst_oid(lc)
Definition: pg_list.h:192
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:15814

◆ check_new_partition_bound()

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

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

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

◆ compute_partition_hash_value()

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

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

2747 {
2748  int i;
2749  uint64 rowHash = 0;
2751 
2752  for (i = 0; i < partnatts; i++)
2753  {
2754  /* Nulls are just ignored */
2755  if (!isnull[i])
2756  {
2757  Datum hash;
2758 
2759  Assert(OidIsValid(partsupfunc[i].fn_oid));
2760 
2761  /*
2762  * Compute hash for each datum value by calling respective
2763  * datatype-specific hash functions of each partition key
2764  * attribute.
2765  */
2766  hash = FunctionCall2Coll(&partsupfunc[i], partcollation[i],
2767  values[i], seed);
2768 
2769  /* Form a single 64-bit hash value */
2770  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
2771  }
2772  }
2773 
2774  return rowHash;
2775 }
#define UInt64GetDatum(X)
Definition: postgres.h:648
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1150
static uint64 hash_combine64(uint64 a, uint64 b)
Definition: hashfn.h:80
#define OidIsValid(objectId)
Definition: c.h:644
uintptr_t Datum
Definition: postgres.h:367
#define DatumGetUInt64(X)
Definition: postgres.h:634
#define Assert(condition)
Definition: c.h:738
#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 225 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().

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

◆ create_list_bounds()

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

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

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

◆ create_range_bounds()

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

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

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

1395 {
1396  Assert(bound && bound->strategy == PARTITION_STRATEGY_HASH);
1397  Assert(bound->datums && bound->ndatums > 0);
1398  Assert(DatumGetInt32(bound->datums[bound->ndatums - 1][0]) > 0);
1399 
1400  return DatumGetInt32(bound->datums[bound->ndatums - 1][0]);
1401 }
#define DatumGetInt32(X)
Definition: postgres.h:472
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:798
#define Assert(condition)
Definition: c.h:738

◆ get_partition_bound_num_indexes()

static int get_partition_bound_num_indexes ( PartitionBoundInfo  b)
static

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

1804 {
1805  int num_indexes;
1806 
1807  Assert(bound);
1808 
1809  switch (bound->strategy)
1810  {
1812 
1813  /*
1814  * The number of the entries in the indexes array is same as the
1815  * greatest modulus.
1816  */
1817  num_indexes = get_hash_partition_greatest_modulus(bound);
1818  break;
1819 
1821  num_indexes = bound->ndatums;
1822  break;
1823 
1825  /* Range partitioned table has an extra index. */
1826  num_indexes = bound->ndatums + 1;
1827  break;
1828 
1829  default:
1830  elog(ERROR, "unexpected partition strategy: %d",
1831  (int) bound->strategy);
1832  }
1833 
1834  return num_indexes;
1835 }
#define ERROR
Definition: elog.h:43
int get_hash_partition_greatest_modulus(PartitionBoundInfo bound)
Definition: partbounds.c:1394
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:798
#define Assert(condition)
Definition: c.h:738
#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 1848 of file partbounds.c.

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

Referenced by make_partition_op_expr().

1850 {
1851  Oid operoid;
1852 
1853  /*
1854  * Get the operator in the partitioning opfamily using the opclass'
1855  * declared input type as both left- and righttype.
1856  */
1857  operoid = get_opfamily_member(key->partopfamily[col],
1858  key->partopcintype[col],
1859  key->partopcintype[col],
1860  strategy);
1861  if (!OidIsValid(operoid))
1862  elog(ERROR, "missing operator %d(%u,%u) in partition opfamily %u",
1863  strategy, key->partopcintype[col], key->partopcintype[col],
1864  key->partopfamily[col]);
1865 
1866  /*
1867  * If the partition key column is not of the same type as the operator
1868  * class and not polymorphic, tell caller to wrap the non-Const expression
1869  * in a RelabelType. This matches what parse_coerce.c does.
1870  */
1871  *need_relabel = (key->parttypid[col] != key->partopcintype[col] &&
1872  key->partopcintype[col] != RECORDOID &&
1873  !IsPolymorphicType(key->partopcintype[col]));
1874 
1875  return operoid;
1876 }
Oid * partopfamily
Definition: partcache.h:33
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:644
#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 1997 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().

1998 {
2000  FuncExpr *fexpr;
2001  Node *relidConst;
2002  Node *modulusConst;
2003  Node *remainderConst;
2004  List *args;
2005  ListCell *partexprs_item;
2006  int i;
2007 
2008  /* Fixed arguments. */
2009  relidConst = (Node *) makeConst(OIDOID,
2010  -1,
2011  InvalidOid,
2012  sizeof(Oid),
2014  false,
2015  true);
2016 
2017  modulusConst = (Node *) makeConst(INT4OID,
2018  -1,
2019  InvalidOid,
2020  sizeof(int32),
2021  Int32GetDatum(spec->modulus),
2022  false,
2023  true);
2024 
2025  remainderConst = (Node *) makeConst(INT4OID,
2026  -1,
2027  InvalidOid,
2028  sizeof(int32),
2029  Int32GetDatum(spec->remainder),
2030  false,
2031  true);
2032 
2033  args = list_make3(relidConst, modulusConst, remainderConst);
2034  partexprs_item = list_head(key->partexprs);
2035 
2036  /* Add an argument for each key column. */
2037  for (i = 0; i < key->partnatts; i++)
2038  {
2039  Node *keyCol;
2040 
2041  /* Left operand */
2042  if (key->partattrs[i] != 0)
2043  {
2044  keyCol = (Node *) makeVar(1,
2045  key->partattrs[i],
2046  key->parttypid[i],
2047  key->parttypmod[i],
2048  key->parttypcoll[i],
2049  0);
2050  }
2051  else
2052  {
2053  keyCol = (Node *) copyObject(lfirst(partexprs_item));
2054  partexprs_item = lnext(key->partexprs, partexprs_item);
2055  }
2056 
2057  args = lappend(args, keyCol);
2058  }
2059 
2060  fexpr = makeFuncExpr(F_SATISFIES_HASH_PARTITION,
2061  BOOLOID,
2062  args,
2063  InvalidOid,
2064  InvalidOid,
2066 
2067  return list_make1(fexpr);
2068 }
#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
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
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:355
#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 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:428
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 2080 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().

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

◆ get_qual_for_range()

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

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

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

◆ get_qual_from_partbound()

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

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

119 {
121  List *my_qual = NIL;
122 
123  Assert(key != NULL);
124 
125  switch (key->strategy)
126  {
129  my_qual = get_qual_for_hash(parent, spec);
130  break;
131 
134  my_qual = get_qual_for_list(parent, spec);
135  break;
136 
139  my_qual = get_qual_for_range(parent, spec, false);
140  break;
141 
142  default:
143  elog(ERROR, "unexpected partition strategy: %d",
144  (int) key->strategy);
145  }
146 
147  return my_qual;
148 }
#define NIL
Definition: pg_list.h:65
static List * get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:1997
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
#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:2289
#define Assert(condition)
Definition: c.h:738
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:799
#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:2080

◆ 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 2655 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().

2661 {
2662  /* Get partition key expression for this column */
2663  if (key->partattrs[keynum] != 0)
2664  {
2665  *keyCol = (Expr *) makeVar(1,
2666  key->partattrs[keynum],
2667  key->parttypid[keynum],
2668  key->parttypmod[keynum],
2669  key->parttypcoll[keynum],
2670  0);
2671  }
2672  else
2673  {
2674  if (*partexprs_item == NULL)
2675  elog(ERROR, "wrong number of partition key expressions");
2676  *keyCol = copyObject(lfirst(*partexprs_item));
2677  *partexprs_item = lnext(key->partexprs, *partexprs_item);
2678  }
2679 
2680  /* Get appropriate Const nodes for the bounds */
2681  if (ldatum->kind == PARTITION_RANGE_DATUM_VALUE)
2682  *lower_val = castNode(Const, copyObject(ldatum->value));
2683  else
2684  *lower_val = NULL;
2685 
2686  if (udatum->kind == PARTITION_RANGE_DATUM_VALUE)
2687  *upper_val = castNode(Const, copyObject(udatum->value));
2688  else
2689  *upper_val = NULL;
2690 }
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 2699 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().

2700 {
2701  List *result = NIL;
2702  NullTest *nulltest;
2703  ListCell *partexprs_item;
2704  int i;
2705 
2706  partexprs_item = list_head(key->partexprs);
2707  for (i = 0; i < key->partnatts; i++)
2708  {
2709  Expr *keyCol;
2710 
2711  if (key->partattrs[i] != 0)
2712  {
2713  keyCol = (Expr *) makeVar(1,
2714  key->partattrs[i],
2715  key->parttypid[i],
2716  key->parttypmod[i],
2717  key->parttypcoll[i],
2718  0);
2719  }
2720  else
2721  {
2722  if (partexprs_item == NULL)
2723  elog(ERROR, "wrong number of partition key expressions");
2724  keyCol = copyObject(lfirst(partexprs_item));
2725  partexprs_item = lnext(key->partexprs, partexprs_item);
2726  }
2727 
2728  nulltest = makeNode(NullTest);
2729  nulltest->arg = keyCol;
2730  nulltest->nulltesttype = IS_NOT_NULL;
2731  nulltest->argisrow = false;
2732  nulltest->location = -1;
2733  result = lappend(result, nulltest);
2734  }
2735 
2736  return result;
2737 }
#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:1219
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:1220
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:1222
#define elog(elevel,...)
Definition: elog.h:228
int i
bool argisrow
Definition: primnodes.h:1221
#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 1411 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().

1412 {
1413  PartitionRangeBound *bound;
1414  ListCell *lc;
1415  int i;
1416 
1417  Assert(datums != NIL);
1418 
1419  bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
1420  bound->index = index;
1421  bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
1422  bound->kind = (PartitionRangeDatumKind *) palloc0(key->partnatts *
1423  sizeof(PartitionRangeDatumKind));
1424  bound->lower = lower;
1425 
1426  i = 0;
1427  foreach(lc, datums)
1428  {
1430 
1431  /* What's contained in this range datum? */
1432  bound->kind[i] = datum->kind;
1433 
1434  if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
1435  {
1436  Const *val = castNode(Const, datum->value);
1437 
1438  if (val->constisnull)
1439  elog(ERROR, "invalid range bound datum");
1440  bound->datums[i] = val->constvalue;
1441  }
1442 
1443  i++;
1444  }
1445 
1446  return bound;
1447 }
Datum constvalue
Definition: primnodes.h:214
#define NIL
Definition: pg_list.h:65
PartitionRangeDatumKind * kind
Definition: partbounds.c:67
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:738
#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:215

◆ make_partition_op_expr()

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

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

1886 {
1887  Oid operoid;
1888  bool need_relabel = false;
1889  Expr *result = NULL;
1890 
1891  /* Get the correct btree operator for this partitioning column */
1892  operoid = get_partition_operator(key, keynum, strategy, &need_relabel);
1893 
1894  /*
1895  * Chosen operator may be such that the non-Const operand needs to be
1896  * coerced, so apply the same; see the comment in
1897  * get_partition_operator().
1898  */
1899  if (!IsA(arg1, Const) &&
1900  (need_relabel ||
1901  key->partcollation[keynum] != key->parttypcoll[keynum]))
1902  arg1 = (Expr *) makeRelabelType(arg1,
1903  key->partopcintype[keynum],
1904  -1,
1905  key->partcollation[keynum],
1907 
1908  /* Generate the actual expression */
1909  switch (key->strategy)
1910  {
1912  {
1913  List *elems = (List *) arg2;
1914  int nelems = list_length(elems);
1915 
1916  Assert(nelems >= 1);
1917  Assert(keynum == 0);
1918 
1919  if (nelems > 1 &&
1920  !type_is_array(key->parttypid[keynum]))
1921  {
1922  ArrayExpr *arrexpr;
1923  ScalarArrayOpExpr *saopexpr;
1924 
1925  /* Construct an ArrayExpr for the right-hand inputs */
1926  arrexpr = makeNode(ArrayExpr);
1927  arrexpr->array_typeid =
1928  get_array_type(key->parttypid[keynum]);
1929  arrexpr->array_collid = key->parttypcoll[keynum];
1930  arrexpr->element_typeid = key->parttypid[keynum];
1931  arrexpr->elements = elems;
1932  arrexpr->multidims = false;
1933  arrexpr->location = -1;
1934 
1935  /* Build leftop = ANY (rightop) */
1936  saopexpr = makeNode(ScalarArrayOpExpr);
1937  saopexpr->opno = operoid;
1938  saopexpr->opfuncid = get_opcode(operoid);
1939  saopexpr->useOr = true;
1940  saopexpr->inputcollid = key->partcollation[keynum];
1941  saopexpr->args = list_make2(arg1, arrexpr);
1942  saopexpr->location = -1;
1943 
1944  result = (Expr *) saopexpr;
1945  }
1946  else
1947  {
1948  List *elemops = NIL;
1949  ListCell *lc;
1950 
1951  foreach(lc, elems)
1952  {
1953  Expr *elem = lfirst(lc),
1954  *elemop;
1955 
1956  elemop = make_opclause(operoid,
1957  BOOLOID,
1958  false,
1959  arg1, elem,
1960  InvalidOid,
1961  key->partcollation[keynum]);
1962  elemops = lappend(elemops, elemop);
1963  }
1964 
1965  result = nelems > 1 ? makeBoolExpr(OR_EXPR, elemops, -1) : linitial(elemops);
1966  }
1967  break;
1968  }
1969 
1971  result = make_opclause(operoid,
1972  BOOLOID,
1973  false,
1974  arg1, arg2,
1975  InvalidOid,
1976  key->partcollation[keynum]);
1977  break;
1978 
1979  default:
1980  elog(ERROR, "invalid partitioning strategy");
1981  break;
1982  }
1983 
1984  return result;
1985 }
#define list_make2(x1, x2)
Definition: pg_list.h:229
bool multidims
Definition: primnodes.h:992
#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:988
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:991
List * lappend(List *list, void *datum)
Definition: list.c:322
Oid * partcollation
Definition: partcache.h:38
int location
Definition: primnodes.h:993
static Oid get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel)
Definition: partbounds.c:1848
#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:738
#define lfirst(lc)
Definition: pg_list.h:190
Oid array_collid
Definition: primnodes.h:989
static int list_length(const List *l)
Definition: pg_list.h:169
#define type_is_array(typid)
Definition: lsyscache.h:185
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:799
Oid element_typeid
Definition: primnodes.h:990
#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 784 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().

786 {
788  int i;
789  int ndatums;
790  int partnatts;
791  int num_indexes;
792  bool hash_part;
793  int natts;
794 
796 
797  dest->strategy = src->strategy;
798  ndatums = dest->ndatums = src->ndatums;
799  partnatts = key->partnatts;
800 
801  num_indexes = get_partition_bound_num_indexes(src);
802 
803  /* List partitioned tables have only a single partition key. */
804  Assert(key->strategy != PARTITION_STRATEGY_LIST || partnatts == 1);
805 
806  dest->datums = (Datum **) palloc(sizeof(Datum *) * ndatums);
807 
808  if (src->kind != NULL)
809  {
810  dest->kind = (PartitionRangeDatumKind **) palloc(ndatums *
811  sizeof(PartitionRangeDatumKind *));
812  for (i = 0; i < ndatums; i++)
813  {
814  dest->kind[i] = (PartitionRangeDatumKind *) palloc(partnatts *
815  sizeof(PartitionRangeDatumKind));
816 
817  memcpy(dest->kind[i], src->kind[i],
818  sizeof(PartitionRangeDatumKind) * key->partnatts);
819  }
820  }
821  else
822  dest->kind = NULL;
823 
824  /*
825  * For hash partitioning, datums array will have two elements - modulus and
826  * remainder.
827  */
828  hash_part = (key->strategy == PARTITION_STRATEGY_HASH);
829  natts = hash_part ? 2 : partnatts;
830 
831  for (i = 0; i < ndatums; i++)
832  {
833  int j;
834 
835  dest->datums[i] = (Datum *) palloc(sizeof(Datum) * natts);
836 
837  for (j = 0; j < natts; j++)
838  {
839  bool byval;
840  int typlen;
841 
842  if (hash_part)
843  {
844  typlen = sizeof(int32); /* Always int4 */
845  byval = true; /* int4 is pass-by-value */
846  }
847  else
848  {
849  byval = key->parttypbyval[j];
850  typlen = key->parttyplen[j];
851  }
852 
853  if (dest->kind == NULL ||
854  dest->kind[i][j] == PARTITION_RANGE_DATUM_VALUE)
855  dest->datums[i][j] = datumCopy(src->datums[i][j],
856  byval, typlen);
857  }
858  }
859 
860  dest->indexes = (int *) palloc(sizeof(int) * num_indexes);
861  memcpy(dest->indexes, src->indexes, sizeof(int) * num_indexes);
862 
863  dest->null_index = src->null_index;
864  dest->default_index = src->default_index;
865 
866  return dest;
867 }
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
PartitionRangeDatumKind
Definition: parsenodes.h:834
signed int int32
Definition: c.h:355
static int get_partition_bound_num_indexes(PartitionBoundInfo b)
Definition: partbounds.c:1803
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:131
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:738
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 172 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().

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

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

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

Referenced by check_new_partition_bound().

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

◆ partition_hbound_cmp()

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

Definition at line 1562 of file partbounds.c.

Referenced by partition_hash_bsearch(), and qsort_partition_hbound_cmp().

1563 {
1564  if (modulus1 < modulus2)
1565  return -1;
1566  if (modulus1 > modulus2)
1567  return 1;
1568  if (modulus1 == modulus2 && remainder1 != remainder2)
1569  return (remainder1 > remainder2) ? 1 : -1;
1570  return 0;
1571 }

◆ partition_list_bsearch()

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

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

1585 {
1586  int lo,
1587  hi,
1588  mid;
1589 
1590  lo = -1;
1591  hi = boundinfo->ndatums - 1;
1592  while (lo < hi)
1593  {
1594  int32 cmpval;
1595 
1596  mid = (lo + hi + 1) / 2;
1597  cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
1598  partcollation[0],
1599  boundinfo->datums[mid][0],
1600  value));
1601  if (cmpval <= 0)
1602  {
1603  lo = mid;
1604  *is_equal = (cmpval == 0);
1605  if (*is_equal)
1606  break;
1607  }
1608  else
1609  hi = mid - 1;
1610  }
1611 
1612  return lo;
1613 }
#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:355
static struct @143 value

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

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

Referenced by check_new_partition_bound().

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

◆ 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 1671 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().

1674 {
1675  int lo,
1676  hi,
1677  mid;
1678 
1679  lo = -1;
1680  hi = boundinfo->ndatums - 1;
1681  while (lo < hi)
1682  {
1683  int32 cmpval;
1684 
1685  mid = (lo + hi + 1) / 2;
1686  cmpval = partition_rbound_datum_cmp(partsupfunc,
1687  partcollation,
1688  boundinfo->datums[mid],
1689  boundinfo->kind[mid],
1690  values,
1691  nvalues);
1692  if (cmpval <= 0)
1693  {
1694  lo = mid;
1695  *is_equal = (cmpval == 0);
1696 
1697  if (*is_equal)
1698  break;
1699  }
1700  else
1701  hi = mid - 1;
1702  }
1703 
1704  return lo;
1705 }
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
signed int int32
Definition: c.h:355
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:1531
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 1467 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().

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

1534 {
1535  int i;
1536  int32 cmpval = -1;
1537 
1538  for (i = 0; i < n_tuple_datums; i++)
1539  {
1540  if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
1541  return -1;
1542  else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
1543  return 1;
1544 
1545  cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
1546  partcollation[i],
1547  rb_datums[i],
1548  tuple_datums[i]));
1549  if (cmpval != 0)
1550  break;
1551  }
1552 
1553  return cmpval;
1554 }
#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:355
int i

◆ partitions_are_ordered()

bool partitions_are_ordered ( PartitionBoundInfo  boundinfo,
int  nparts 
)

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

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

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

Referenced by create_hash_bounds().

1755 {
1756  PartitionHashBound *h1 = (*(PartitionHashBound *const *) a);
1757  PartitionHashBound *h2 = (*(PartitionHashBound *const *) b);
1758 
1759  return partition_hbound_cmp(h1->modulus, h1->remainder,
1760  h2->modulus, h2->remainder);
1761 }
static int32 partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
Definition: partbounds.c:1562

◆ qsort_partition_list_value_cmp()

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

Definition at line 1769 of file partbounds.c.

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

Referenced by create_list_bounds().

1770 {
1771  Datum val1 = (*(PartitionListValue *const *) a)->value,
1772  val2 = (*(PartitionListValue *const *) b)->value;
1774 
1776  key->partcollation[0],
1777  val1, val2));
1778 }
#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 1786 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().

1787 {
1788  PartitionRangeBound *b1 = (*(PartitionRangeBound *const *) a);
1789  PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
1791 
1792  return partition_rbound_cmp(key->partnatts, key->partsupfunc,
1793  key->partcollation, b1->datums, b1->kind,
1794  b1->lower, b2);
1795 }
PartitionRangeDatumKind * kind
Definition: partbounds.c:67
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:1467
void * arg
struct PartitionKeyData * PartitionKey
Definition: partdefs.h:18

◆ satisfies_hash_partition()

Datum satisfies_hash_partition ( PG_FUNCTION_ARGS  )

Definition at line 2792 of file partbounds.c.

References AccessShareLock, ARR_ELEMTYPE, DatumGetUInt64, deconstruct_array(), ereport, errcode(), errmsg(), ERROR, FLEXIBLE_ARRAY_MEMBER, 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.

2793 {
2794  typedef struct ColumnsHashData
2795  {
2796  Oid relid;
2797  int nkeys;
2798  Oid variadic_type;
2799  int16 variadic_typlen;
2800  bool variadic_typbyval;
2801  char variadic_typalign;
2802  Oid partcollid[PARTITION_MAX_KEYS];
2803  FmgrInfo partsupfunc[FLEXIBLE_ARRAY_MEMBER];
2804  } ColumnsHashData;
2805  Oid parentId;
2806  int modulus;
2807  int remainder;
2809  ColumnsHashData *my_extra;
2810  uint64 rowHash = 0;
2811 
2812  /* Return null if the parent OID, modulus, or remainder is NULL. */
2813  if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2))
2814  PG_RETURN_NULL();
2815  parentId = PG_GETARG_OID(0);
2816  modulus = PG_GETARG_INT32(1);
2817  remainder = PG_GETARG_INT32(2);
2818 
2819  /* Sanity check modulus and remainder. */
2820  if (modulus <= 0)
2821  ereport(ERROR,
2822  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2823  errmsg("modulus for hash partition must be a positive integer")));
2824  if (remainder < 0)
2825  ereport(ERROR,
2826  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2827  errmsg("remainder for hash partition must be a non-negative integer")));
2828  if (remainder >= modulus)
2829  ereport(ERROR,
2830  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2831  errmsg("remainder for hash partition must be less than modulus")));
2832 
2833  /*
2834  * Cache hash function information.
2835  */
2836  my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
2837  if (my_extra == NULL || my_extra->relid != parentId)
2838  {
2839  Relation parent;
2840  PartitionKey key;
2841  int j;
2842 
2843  /* Open parent relation and fetch partition key info */
2844  parent = try_relation_open(parentId, AccessShareLock);
2845  if (parent == NULL)
2846  PG_RETURN_NULL();
2847  key = RelationGetPartitionKey(parent);
2848 
2849  /* Reject parent table that is not hash-partitioned. */
2850  if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE ||
2852  ereport(ERROR,
2853  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2854  errmsg("\"%s\" is not a hash partitioned table",
2855  get_rel_name(parentId))));
2856 
2857  if (!get_fn_expr_variadic(fcinfo->flinfo))
2858  {
2859  int nargs = PG_NARGS() - 3;
2860 
2861  /* complain if wrong number of column values */
2862  if (key->partnatts != nargs)
2863  ereport(ERROR,
2864  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2865  errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
2866  key->partnatts, nargs)));
2867 
2868  /* allocate space for our cache */
2869  fcinfo->flinfo->fn_extra =
2870  MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
2871  offsetof(ColumnsHashData, partsupfunc) +
2872  sizeof(FmgrInfo) * nargs);
2873  my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
2874  my_extra->relid = parentId;
2875  my_extra->nkeys = key->partnatts;
2876  memcpy(my_extra->partcollid, key->partcollation,
2877  key->partnatts * sizeof(Oid));
2878 
2879  /* check argument types and save fmgr_infos */
2880  for (j = 0; j < key->partnatts; ++j)
2881  {
2882  Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, j + 3);
2883 
2884  if (argtype != key->parttypid[j] && !IsBinaryCoercible(argtype, key->parttypid[j]))
2885  ereport(ERROR,
2886  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2887  errmsg("column %d of the partition key has type \"%s\", but supplied value is of type \"%s\"",
2888  j + 1, format_type_be(key->parttypid[j]), format_type_be(argtype))));
2889 
2890  fmgr_info_copy(&my_extra->partsupfunc[j],
2891  &key->partsupfunc[j],
2892  fcinfo->flinfo->fn_mcxt);
2893  }
2894  }
2895  else
2896  {
2897  ArrayType *variadic_array = PG_GETARG_ARRAYTYPE_P(3);
2898 
2899  /* allocate space for our cache -- just one FmgrInfo in this case */
2900  fcinfo->flinfo->fn_extra =
2901  MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
2902  offsetof(ColumnsHashData, partsupfunc) +
2903  sizeof(FmgrInfo));
2904  my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
2905  my_extra->relid = parentId;
2906  my_extra->nkeys = key->partnatts;
2907  my_extra->variadic_type = ARR_ELEMTYPE(variadic_array);
2908  get_typlenbyvalalign(my_extra->variadic_type,
2909  &my_extra->variadic_typlen,
2910  &my_extra->variadic_typbyval,
2911  &my_extra->variadic_typalign);
2912  my_extra->partcollid[0] = key->partcollation[0];
2913 
2914  /* check argument types */
2915  for (j = 0; j < key->partnatts; ++j)
2916  if (key->parttypid[j] != my_extra->variadic_type)
2917  ereport(ERROR,
2918  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2919  errmsg("column %d of the partition key has type \"%s\", but supplied value is of type \"%s\"",
2920  j + 1,
2921  format_type_be(key->parttypid[j]),
2922  format_type_be(my_extra->variadic_type))));
2923 
2924  fmgr_info_copy(&my_extra->partsupfunc[0],
2925  &key->partsupfunc[0],
2926  fcinfo->flinfo->fn_mcxt);
2927  }
2928 
2929  /* Hold lock until commit */
2930  relation_close(parent, NoLock);
2931  }
2932 
2933  if (!OidIsValid(my_extra->variadic_type))
2934  {
2935  int nkeys = my_extra->nkeys;
2936  int i;
2937 
2938  /*
2939  * For a non-variadic call, neither the number of arguments nor their
2940  * types can change across calls, so avoid the expense of rechecking
2941  * here.
2942  */
2943 
2944  for (i = 0; i < nkeys; i++)
2945  {
2946  Datum hash;
2947 
2948  /* keys start from fourth argument of function. */
2949  int argno = i + 3;
2950 
2951  if (PG_ARGISNULL(argno))
2952  continue;
2953 
2954  hash = FunctionCall2Coll(&my_extra->partsupfunc[i],
2955  my_extra->partcollid[i],
2956  PG_GETARG_DATUM(argno),
2957  seed);
2958 
2959  /* Form a single 64-bit hash value */
2960  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
2961  }
2962  }
2963  else
2964  {
2965  ArrayType *variadic_array = PG_GETARG_ARRAYTYPE_P(3);
2966  int i;
2967  int nelems;
2968  Datum *datum;
2969  bool *isnull;
2970 
2971  deconstruct_array(variadic_array,
2972  my_extra->variadic_type,
2973  my_extra->variadic_typlen,
2974  my_extra->variadic_typbyval,
2975  my_extra->variadic_typalign,
2976  &datum, &isnull, &nelems);
2977 
2978  /* complain if wrong number of column values */
2979  if (nelems != my_extra->nkeys)
2980  ereport(ERROR,
2981  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2982  errmsg("number of partitioning columns (%d) does not match number of partition keys provided (%d)",
2983  my_extra->nkeys, nelems)));
2984 
2985  for (i = 0; i < nelems; i++)
2986  {
2987  Datum hash;
2988 
2989  if (isnull[i])
2990  continue;
2991 
2992  hash = FunctionCall2Coll(&my_extra->partsupfunc[0],
2993  my_extra->partcollid[0],
2994  datum[i],
2995  seed);
2996 
2997  /* Form a single 64-bit hash value */
2998  rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
2999  }
3000  }
3001 
3002  PG_RETURN_BOOL(rowHash % modulus == remainder);
3003 }
signed short int16
Definition: c.h:354
#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
#define FLEXIBLE_ARRAY_MEMBER
Definition: c.h:276
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
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
Form_pg_class rd_rel
Definition: rel.h:84
unsigned int Oid
Definition: postgres_ext.h:31
static uint64 hash_combine64(uint64 a, uint64 b)
Definition: hashfn.h:80
#define OidIsValid(objectId)
Definition: c.h:644
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)
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
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:661