PostgreSQL Source Code  git master
partbounds.c File Reference
#include "postgres.h"
#include "access/relation.h"
#include "access/table.h"
#include "access/tableam.h"
#include "catalog/partition.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_type.h"
#include "commands/tablecmds.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_coerce.h"
#include "partitioning/partbounds.h"
#include "partitioning/partdesc.h"
#include "partitioning/partprune.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/hashutils.h"
#include "utils/lsyscache.h"
#include "utils/partcache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/ruleutils.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 1227 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().

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

◆ check_new_partition_bound()

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

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

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

◆ compute_partition_hash_value()

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

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

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

◆ create_hash_bounds()

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

Definition at line 226 of file partbounds.c.

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

Referenced by partition_bounds_create().

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

◆ create_list_bounds()

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

Definition at line 308 of file partbounds.c.

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

Referenced by partition_bounds_create().

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

◆ create_range_bounds()

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

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

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

◆ get_hash_partition_greatest_modulus()

int get_hash_partition_greatest_modulus ( PartitionBoundInfo  bound)

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

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

◆ get_partition_bound_num_indexes()

static int get_partition_bound_num_indexes ( PartitionBoundInfo  b)
static

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

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

◆ get_partition_operator()

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

Definition at line 1843 of file partbounds.c.

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

Referenced by make_partition_op_expr().

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

◆ get_qual_for_hash()

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

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

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

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

◆ get_qual_for_range()

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

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

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

◆ get_qual_from_partbound()

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

Definition at line 118 of file partbounds.c.

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

Referenced by ATExecAttachPartition(), and generate_partition_qual().

120 {
122  List *my_qual = NIL;
123 
124  Assert(key != NULL);
125 
126  switch (key->strategy)
127  {
130  my_qual = get_qual_for_hash(parent, spec);
131  break;
132 
135  my_qual = get_qual_for_list(parent, spec);
136  break;
137 
140  my_qual = get_qual_for_range(parent, spec, false);
141  break;
142 
143  default:
144  elog(ERROR, "unexpected partition strategy: %d",
145  (int) key->strategy);
146  }
147 
148  return my_qual;
149 }
#define NIL
Definition: pg_list.h:65
static List * get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:1992
#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:2284
#define Assert(condition)
Definition: c.h:732
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:799
#define RelationGetPartitionKey(relation)
Definition: rel.h:600
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:800
#define elog(elevel,...)
Definition: elog.h:226
Definition: pg_list.h:50
static List * get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:2075

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

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

◆ get_range_nulltest()

static List * get_range_nulltest ( PartitionKey  key)
static

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

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

◆ make_one_partition_rbound()

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

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

1407 {
1408  PartitionRangeBound *bound;
1409  ListCell *lc;
1410  int i;
1411 
1412  Assert(datums != NIL);
1413 
1414  bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
1415  bound->index = index;
1416  bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
1417  bound->kind = (PartitionRangeDatumKind *) palloc0(key->partnatts *
1418  sizeof(PartitionRangeDatumKind));
1419  bound->lower = lower;
1420 
1421  i = 0;
1422  foreach(lc, datums)
1423  {
1425 
1426  /* What's contained in this range datum? */
1427  bound->kind[i] = datum->kind;
1428 
1429  if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
1430  {
1431  Const *val = castNode(Const, datum->value);
1432 
1433  if (val->constisnull)
1434  elog(ERROR, "invalid range bound datum");
1435  bound->datums[i] = val->constvalue;
1436  }
1437 
1438  i++;
1439  }
1440 
1441  return bound;
1442 }
Datum constvalue
Definition: primnodes.h:200
#define NIL
Definition: pg_list.h:65
PartitionRangeDatumKind * kind
Definition: partbounds.c:68
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:44
#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:732
#define lfirst(lc)
Definition: pg_list.h:190
#define elog(elevel,...)
Definition: elog.h:226
int i
long val
Definition: informix.c:684
bool constisnull
Definition: primnodes.h:201

◆ make_partition_op_expr()

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

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

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

◆ partition_bounds_copy()

PartitionBoundInfo partition_bounds_copy ( PartitionBoundInfo  src,
PartitionKey  key 
)

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

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

◆ partition_bounds_create()

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

Definition at line 173 of file partbounds.c.

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

Referenced by RelationBuildPartitionDesc().

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

◆ partition_bounds_equal()

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

Definition at line 668 of file partbounds.c.

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

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

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

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

Referenced by check_new_partition_bound().

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

◆ partition_hbound_cmp()

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

Definition at line 1557 of file partbounds.c.

Referenced by partition_hash_bsearch(), and qsort_partition_hbound_cmp().

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

◆ partition_list_bsearch()

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

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

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

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

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

Referenced by check_new_partition_bound().

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

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

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

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

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

◆ partitions_are_ordered()

bool partitions_are_ordered ( PartitionBoundInfo  boundinfo,
int  nparts 
)

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

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

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

Referenced by create_hash_bounds().

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

◆ qsort_partition_list_value_cmp()

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

Definition at line 1764 of file partbounds.c.

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

Referenced by create_list_bounds().

1765 {
1766  Datum val1 = (*(PartitionListValue *const *) a)->value,
1767  val2 = (*(PartitionListValue *const *) b)->value;
1769 
1771  key->partcollation[0],
1772  val1, val2));
1773 }
#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 1781 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().

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

◆ satisfies_hash_partition()

Datum satisfies_hash_partition ( PG_FUNCTION_ARGS  )

Definition at line 2787 of file partbounds.c.

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

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