PostgreSQL Source Code  git master
indexcmds.c File Reference
#include "postgres.h"
#include "access/amapi.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "access/sysattr.h"
#include "access/tableam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/pg_am.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/progress.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "partitioning/partdesc.h"
#include "pgstat.h"
#include "rewrite/rewriteManip.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "storage/sinvaladt.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/partcache.h"
#include "utils/pg_rusage.h"
#include "utils/regproc.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
Include dependency graph for indexcmds.c:

Go to the source code of this file.

Data Structures

struct  ReindexIndexCallbackState
 
struct  ReindexErrorInfo
 

Typedefs

typedef struct ReindexErrorInfo ReindexErrorInfo
 

Functions

static bool CompareOpclassOptions (Datum *opts1, Datum *opts2, int natts)
 
static void CheckPredicate (Expr *predicate)
 
static void ComputeIndexAttrs (IndexInfo *indexInfo, Oid *typeOidP, Oid *collationOidP, Oid *classOidP, int16 *colOptionP, List *attList, List *exclusionOpNames, Oid relId, const char *accessMethodName, Oid accessMethodId, bool amcanorder, bool isconstraint, Oid ddl_userid, int ddl_sec_context, int *ddl_save_nestlevel)
 
static char * ChooseIndexName (const char *tabname, Oid namespaceId, List *colnames, List *exclusionOpNames, bool primary, bool isconstraint)
 
static char * ChooseIndexNameAddition (List *colnames)
 
static ListChooseIndexColumnNames (List *indexElems)
 
static void ReindexIndex (RangeVar *indexRelation, ReindexParams *params, bool isTopLevel)
 
static void RangeVarCallbackForReindexIndex (const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
 
static Oid ReindexTable (RangeVar *relation, ReindexParams *params, bool isTopLevel)
 
static void ReindexMultipleTables (const char *objectName, ReindexObjectType objectKind, ReindexParams *params)
 
static void reindex_error_callback (void *args)
 
static void ReindexPartitions (Oid relid, ReindexParams *params, bool isTopLevel)
 
static void ReindexMultipleInternal (List *relids, ReindexParams *params)
 
static bool ReindexRelationConcurrently (Oid relationOid, ReindexParams *params)
 
static void update_relispartition (Oid relationId, bool newval)
 
static void set_indexsafe_procflags (void)
 
bool CheckIndexCompatible (Oid oldId, const char *accessMethodName, List *attributeList, List *exclusionOpNames)
 
void WaitForOlderSnapshots (TransactionId limitXmin, bool progress)
 
ObjectAddress DefineIndex (Oid relationId, IndexStmt *stmt, Oid indexRelationId, Oid parentIndexId, Oid parentConstraintId, bool is_alter_table, bool check_rights, bool check_not_in_use, bool skip_build, bool quiet)
 
static bool CheckMutability (Expr *expr)
 
Oid ResolveOpClass (List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
 
Oid GetDefaultOpClass (Oid type_id, Oid am_id)
 
char * makeObjectName (const char *name1, const char *name2, const char *label)
 
char * ChooseRelationName (const char *name1, const char *name2, const char *label, Oid namespaceid, bool isconstraint)
 
void ExecReindex (ParseState *pstate, ReindexStmt *stmt, bool isTopLevel)
 
void IndexSetParentIndex (Relation partitionIdx, Oid parentOid)
 

Typedef Documentation

◆ ReindexErrorInfo

Function Documentation

◆ CheckIndexCompatible()

bool CheckIndexCompatible ( Oid  oldId,
const char *  accessMethodName,
List attributeList,
List exclusionOpNames 
)

Definition at line 167 of file indexcmds.c.

171 {
172  bool isconstraint;
173  Oid *typeObjectId;
174  Oid *collationObjectId;
175  Oid *classObjectId;
176  Oid accessMethodId;
177  Oid relationId;
178  HeapTuple tuple;
179  Form_pg_index indexForm;
180  Form_pg_am accessMethodForm;
181  IndexAmRoutine *amRoutine;
182  bool amcanorder;
183  int16 *coloptions;
184  IndexInfo *indexInfo;
185  int numberOfAttributes;
186  int old_natts;
187  bool isnull;
188  bool ret = true;
189  oidvector *old_indclass;
190  oidvector *old_indcollation;
191  Relation irel;
192  int i;
193  Datum d;
194 
195  /* Caller should already have the relation locked in some way. */
196  relationId = IndexGetRelation(oldId, false);
197 
198  /*
199  * We can pretend isconstraint = false unconditionally. It only serves to
200  * decide the text of an error message that should never happen for us.
201  */
202  isconstraint = false;
203 
204  numberOfAttributes = list_length(attributeList);
205  Assert(numberOfAttributes > 0);
206  Assert(numberOfAttributes <= INDEX_MAX_KEYS);
207 
208  /* look up the access method */
209  tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
210  if (!HeapTupleIsValid(tuple))
211  ereport(ERROR,
212  (errcode(ERRCODE_UNDEFINED_OBJECT),
213  errmsg("access method \"%s\" does not exist",
214  accessMethodName)));
215  accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
216  accessMethodId = accessMethodForm->oid;
217  amRoutine = GetIndexAmRoutine(accessMethodForm->amhandler);
218  ReleaseSysCache(tuple);
219 
220  amcanorder = amRoutine->amcanorder;
221 
222  /*
223  * Compute the operator classes, collations, and exclusion operators for
224  * the new index, so we can test whether it's compatible with the existing
225  * one. Note that ComputeIndexAttrs might fail here, but that's OK:
226  * DefineIndex would have failed later. Our attributeList contains only
227  * key attributes, thus we're filling ii_NumIndexAttrs and
228  * ii_NumIndexKeyAttrs with same value.
229  */
230  indexInfo = makeIndexInfo(numberOfAttributes, numberOfAttributes,
231  accessMethodId, NIL, NIL, false, false, false, false);
232  typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
233  collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
234  classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
235  coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
236  ComputeIndexAttrs(indexInfo,
237  typeObjectId, collationObjectId, classObjectId,
238  coloptions, attributeList,
239  exclusionOpNames, relationId,
240  accessMethodName, accessMethodId,
241  amcanorder, isconstraint, InvalidOid, 0, NULL);
242 
243 
244  /* Get the soon-obsolete pg_index tuple. */
246  if (!HeapTupleIsValid(tuple))
247  elog(ERROR, "cache lookup failed for index %u", oldId);
248  indexForm = (Form_pg_index) GETSTRUCT(tuple);
249 
250  /*
251  * We don't assess expressions or predicates; assume incompatibility.
252  * Also, if the index is invalid for any reason, treat it as incompatible.
253  */
254  if (!(heap_attisnull(tuple, Anum_pg_index_indpred, NULL) &&
255  heap_attisnull(tuple, Anum_pg_index_indexprs, NULL) &&
256  indexForm->indisvalid))
257  {
258  ReleaseSysCache(tuple);
259  return false;
260  }
261 
262  /* Any change in operator class or collation breaks compatibility. */
263  old_natts = indexForm->indnkeyatts;
264  Assert(old_natts == numberOfAttributes);
265 
266  d = SysCacheGetAttr(INDEXRELID, tuple, Anum_pg_index_indcollation, &isnull);
267  Assert(!isnull);
268  old_indcollation = (oidvector *) DatumGetPointer(d);
269 
270  d = SysCacheGetAttr(INDEXRELID, tuple, Anum_pg_index_indclass, &isnull);
271  Assert(!isnull);
272  old_indclass = (oidvector *) DatumGetPointer(d);
273 
274  ret = (memcmp(old_indclass->values, classObjectId,
275  old_natts * sizeof(Oid)) == 0 &&
276  memcmp(old_indcollation->values, collationObjectId,
277  old_natts * sizeof(Oid)) == 0);
278 
279  ReleaseSysCache(tuple);
280 
281  if (!ret)
282  return false;
283 
284  /* For polymorphic opcintype, column type changes break compatibility. */
285  irel = index_open(oldId, AccessShareLock); /* caller probably has a lock */
286  for (i = 0; i < old_natts; i++)
287  {
288  if (IsPolymorphicType(get_opclass_input_type(classObjectId[i])) &&
289  TupleDescAttr(irel->rd_att, i)->atttypid != typeObjectId[i])
290  {
291  ret = false;
292  break;
293  }
294  }
295 
296  /* Any change in opclass options break compatibility. */
297  if (ret)
298  {
299  Datum *opclassOptions = RelationGetIndexRawAttOptions(irel);
300 
301  ret = CompareOpclassOptions(opclassOptions,
302  indexInfo->ii_OpclassOptions, old_natts);
303 
304  if (opclassOptions)
305  pfree(opclassOptions);
306  }
307 
308  /* Any change in exclusion operator selections breaks compatibility. */
309  if (ret && indexInfo->ii_ExclusionOps != NULL)
310  {
311  Oid *old_operators,
312  *old_procs;
313  uint16 *old_strats;
314 
315  RelationGetExclusionInfo(irel, &old_operators, &old_procs, &old_strats);
316  ret = memcmp(old_operators, indexInfo->ii_ExclusionOps,
317  old_natts * sizeof(Oid)) == 0;
318 
319  /* Require an exact input type match for polymorphic operators. */
320  if (ret)
321  {
322  for (i = 0; i < old_natts && ret; i++)
323  {
324  Oid left,
325  right;
326 
327  op_input_types(indexInfo->ii_ExclusionOps[i], &left, &right);
328  if ((IsPolymorphicType(left) || IsPolymorphicType(right)) &&
329  TupleDescAttr(irel->rd_att, i)->atttypid != typeObjectId[i])
330  {
331  ret = false;
332  break;
333  }
334  }
335  }
336  }
337 
338  index_close(irel, NoLock);
339  return ret;
340 }
IndexAmRoutine * GetIndexAmRoutine(Oid amhandler)
Definition: amapi.c:33
unsigned short uint16
Definition: c.h:440
signed short int16
Definition: c.h:428
int errcode(int sqlerrcode)
Definition: elog.c:693
int errmsg(const char *fmt,...)
Definition: elog.c:904
#define ERROR
Definition: elog.h:33
#define elog(elevel,...)
Definition: elog.h:218
#define ereport(elevel,...)
Definition: elog.h:143
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition: heaptuple.c:359
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:649
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition: index.c:3520
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:158
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:132
static bool CompareOpclassOptions(Datum *opts1, Datum *opts2, int natts)
Definition: indexcmds.c:349
static void ComputeIndexAttrs(IndexInfo *indexInfo, Oid *typeOidP, Oid *collationOidP, Oid *classOidP, int16 *colOptionP, List *attList, List *exclusionOpNames, Oid relId, const char *accessMethodName, Oid accessMethodId, bool amcanorder, bool isconstraint, Oid ddl_userid, int ddl_sec_context, int *ddl_save_nestlevel)
Definition: indexcmds.c:1752
int i
Definition: isn.c:73
Assert(fmt[strlen(fmt) - 1] !='\n')
#define NoLock
Definition: lockdefs.h:34
#define AccessShareLock
Definition: lockdefs.h:36
Oid get_opclass_input_type(Oid opclass)
Definition: lsyscache.c:1215
void op_input_types(Oid opno, Oid *lefttype, Oid *righttype)
Definition: lsyscache.c:1339
IndexInfo * makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions, List *predicates, bool unique, bool nulls_not_distinct, bool isready, bool concurrent)
Definition: makefuncs.c:744
void pfree(void *pointer)
Definition: mcxt.c:1175
void * palloc(Size size)
Definition: mcxt.c:1068
FormData_pg_am * Form_pg_am
Definition: pg_am.h:48
#define INDEX_MAX_KEYS
FormData_pg_index * Form_pg_index
Definition: pg_index.h:70
static int list_length(const List *l)
Definition: pg_list.h:149
#define NIL
Definition: pg_list.h:65
uintptr_t Datum
Definition: postgres.h:411
#define DatumGetPointer(X)
Definition: postgres.h:593
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define PointerGetDatum(X)
Definition: postgres.h:600
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
Datum * RelationGetIndexRawAttOptions(Relation indexrel)
Definition: relcache.c:5706
void RelationGetExclusionInfo(Relation indexRelation, Oid **operators, Oid **procs, uint16 **strategies)
Definition: relcache.c:5423
bool amcanorder
Definition: amapi.h:220
Datum * ii_OpclassOptions
Definition: execnodes.h:174
Oid * ii_ExclusionOps
Definition: execnodes.h:168
TupleDesc rd_att
Definition: rel.h:110
Definition: c.h:661
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:668
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1221
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1173
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1434
@ AMNAME
Definition: syscache.h:35
@ INDEXRELID
Definition: syscache.h:66
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92

References AccessShareLock, IndexAmRoutine::amcanorder, AMNAME, Assert(), CompareOpclassOptions(), ComputeIndexAttrs(), DatumGetPointer, elog, ereport, errcode(), errmsg(), ERROR, get_opclass_input_type(), GetIndexAmRoutine(), GETSTRUCT, heap_attisnull(), HeapTupleIsValid, i, IndexInfo::ii_ExclusionOps, IndexInfo::ii_OpclassOptions, index_close(), INDEX_MAX_KEYS, index_open(), IndexGetRelation(), INDEXRELID, InvalidOid, list_length(), makeIndexInfo(), NIL, NoLock, ObjectIdGetDatum, op_input_types(), palloc(), pfree(), PointerGetDatum, RelationData::rd_att, RelationGetExclusionInfo(), RelationGetIndexRawAttOptions(), ReleaseSysCache(), SearchSysCache1(), SysCacheGetAttr(), TupleDescAttr, and oidvector::values.

Referenced by TryReuseIndex().

◆ CheckMutability()

static bool CheckMutability ( Expr expr)
static

Definition at line 1691 of file indexcmds.c.

1692 {
1693  /*
1694  * First run the expression through the planner. This has a couple of
1695  * important consequences. First, function default arguments will get
1696  * inserted, which may affect volatility (consider "default now()").
1697  * Second, inline-able functions will get inlined, which may allow us to
1698  * conclude that the function is really less volatile than it's marked. As
1699  * an example, polymorphic functions must be marked with the most volatile
1700  * behavior that they have for any input type, but once we inline the
1701  * function we may be able to conclude that it's not so volatile for the
1702  * particular input type we're dealing with.
1703  *
1704  * We assume here that expression_planner() won't scribble on its input.
1705  */
1706  expr = expression_planner(expr);
1707 
1708  /* Now we can search for non-immutable functions */
1709  return contain_mutable_functions((Node *) expr);
1710 }
bool contain_mutable_functions(Node *clause)
Definition: clauses.c:368
Expr * expression_planner(Expr *expr)
Definition: planner.c:5882
Definition: nodes.h:574

References contain_mutable_functions(), and expression_planner().

Referenced by CheckPredicate(), and ComputeIndexAttrs().

◆ CheckPredicate()

static void CheckPredicate ( Expr predicate)
static

Definition at line 1725 of file indexcmds.c.

1726 {
1727  /*
1728  * transformExpr() should have already rejected subqueries, aggregates,
1729  * and window functions, based on the EXPR_KIND_ for a predicate.
1730  */
1731 
1732  /*
1733  * A predicate using mutable functions is probably wrong, for the same
1734  * reasons that we don't allow an index expression to use one.
1735  */
1736  if (CheckMutability(predicate))
1737  ereport(ERROR,
1738  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1739  errmsg("functions in index predicate must be marked IMMUTABLE")));
1740 }
static bool CheckMutability(Expr *expr)
Definition: indexcmds.c:1691

References CheckMutability(), ereport, errcode(), errmsg(), and ERROR.

Referenced by DefineIndex().

◆ ChooseIndexColumnNames()

static List * ChooseIndexColumnNames ( List indexElems)
static

Definition at line 2528 of file indexcmds.c.

2529 {
2530  List *result = NIL;
2531  ListCell *lc;
2532 
2533  foreach(lc, indexElems)
2534  {
2535  IndexElem *ielem = (IndexElem *) lfirst(lc);
2536  const char *origname;
2537  const char *curname;
2538  int i;
2539  char buf[NAMEDATALEN];
2540 
2541  /* Get the preliminary name from the IndexElem */
2542  if (ielem->indexcolname)
2543  origname = ielem->indexcolname; /* caller-specified name */
2544  else if (ielem->name)
2545  origname = ielem->name; /* simple column reference */
2546  else
2547  origname = "expr"; /* default name for expression */
2548 
2549  /* If it conflicts with any previous column, tweak it */
2550  curname = origname;
2551  for (i = 1;; i++)
2552  {
2553  ListCell *lc2;
2554  char nbuf[32];
2555  int nlen;
2556 
2557  foreach(lc2, result)
2558  {
2559  if (strcmp(curname, (char *) lfirst(lc2)) == 0)
2560  break;
2561  }
2562  if (lc2 == NULL)
2563  break; /* found nonconflicting name */
2564 
2565  sprintf(nbuf, "%d", i);
2566 
2567  /* Ensure generated names are shorter than NAMEDATALEN */
2568  nlen = pg_mbcliplen(origname, strlen(origname),
2569  NAMEDATALEN - 1 - strlen(nbuf));
2570  memcpy(buf, origname, nlen);
2571  strcpy(buf + nlen, nbuf);
2572  curname = buf;
2573  }
2574 
2575  /* And attach to the result list */
2576  result = lappend(result, pstrdup(curname));
2577  }
2578  return result;
2579 }
List * lappend(List *list, void *datum)
Definition: list.c:336
int pg_mbcliplen(const char *mbstr, int len, int limit)
Definition: mbutils.c:1026
char * pstrdup(const char *in)
Definition: mcxt.c:1305
#define NAMEDATALEN
#define lfirst(lc)
Definition: pg_list.h:169
static char * buf
Definition: pg_test_fsync.c:67
#define sprintf
Definition: port.h:227
char * indexcolname
Definition: parsenodes.h:735
char * name
Definition: parsenodes.h:733
Definition: pg_list.h:51

References buf, i, IndexElem::indexcolname, lappend(), lfirst, IndexElem::name, NAMEDATALEN, NIL, pg_mbcliplen(), pstrdup(), and sprintf.

Referenced by DefineIndex().

◆ ChooseIndexName()

static char * ChooseIndexName ( const char *  tabname,
Oid  namespaceId,
List colnames,
List exclusionOpNames,
bool  primary,
bool  isconstraint 
)
static

Definition at line 2439 of file indexcmds.c.

2442 {
2443  char *indexname;
2444 
2445  if (primary)
2446  {
2447  /* the primary key's name does not depend on the specific column(s) */
2448  indexname = ChooseRelationName(tabname,
2449  NULL,
2450  "pkey",
2451  namespaceId,
2452  true);
2453  }
2454  else if (exclusionOpNames != NIL)
2455  {
2456  indexname = ChooseRelationName(tabname,
2457  ChooseIndexNameAddition(colnames),
2458  "excl",
2459  namespaceId,
2460  true);
2461  }
2462  else if (isconstraint)
2463  {
2464  indexname = ChooseRelationName(tabname,
2465  ChooseIndexNameAddition(colnames),
2466  "key",
2467  namespaceId,
2468  true);
2469  }
2470  else
2471  {
2472  indexname = ChooseRelationName(tabname,
2473  ChooseIndexNameAddition(colnames),
2474  "idx",
2475  namespaceId,
2476  false);
2477  }
2478 
2479  return indexname;
2480 }
static char * ChooseIndexNameAddition(List *colnames)
Definition: indexcmds.c:2494
char * ChooseRelationName(const char *name1, const char *name2, const char *label, Oid namespaceid, bool isconstraint)
Definition: indexcmds.c:2403

References ChooseIndexNameAddition(), ChooseRelationName(), and NIL.

Referenced by DefineIndex().

◆ ChooseIndexNameAddition()

static char * ChooseIndexNameAddition ( List colnames)
static

Definition at line 2494 of file indexcmds.c.

2495 {
2496  char buf[NAMEDATALEN * 2];
2497  int buflen = 0;
2498  ListCell *lc;
2499 
2500  buf[0] = '\0';
2501  foreach(lc, colnames)
2502  {
2503  const char *name = (const char *) lfirst(lc);
2504 
2505  if (buflen > 0)
2506  buf[buflen++] = '_'; /* insert _ between names */
2507 
2508  /*
2509  * At this point we have buflen <= NAMEDATALEN. name should be less
2510  * than NAMEDATALEN already, but use strlcpy for paranoia.
2511  */
2512  strlcpy(buf + buflen, name, NAMEDATALEN);
2513  buflen += strlen(buf + buflen);
2514  if (buflen >= NAMEDATALEN)
2515  break;
2516  }
2517  return pstrdup(buf);
2518 }
const char * name
Definition: encode.c:561
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45

References buf, lfirst, name, NAMEDATALEN, pstrdup(), and strlcpy().

Referenced by ChooseIndexName().

◆ ChooseRelationName()

char* ChooseRelationName ( const char *  name1,
const char *  name2,
const char *  label,
Oid  namespaceid,
bool  isconstraint 
)

Definition at line 2403 of file indexcmds.c.

2406 {
2407  int pass = 0;
2408  char *relname = NULL;
2409  char modlabel[NAMEDATALEN];
2410 
2411  /* try the unmodified label first */
2412  strlcpy(modlabel, label, sizeof(modlabel));
2413 
2414  for (;;)
2415  {
2416  relname = makeObjectName(name1, name2, modlabel);
2417 
2418  if (!OidIsValid(get_relname_relid(relname, namespaceid)))
2419  {
2420  if (!isconstraint ||
2421  !ConstraintNameExists(relname, namespaceid))
2422  break;
2423  }
2424 
2425  /* found a conflict, so try a new name component */
2426  pfree(relname);
2427  snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
2428  }
2429 
2430  return relname;
2431 }
#define OidIsValid(objectId)
Definition: c.h:710
char * makeObjectName(const char *name1, const char *name2, const char *label)
Definition: indexcmds.c:2317
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:1866
static char * label
NameData relname
Definition: pg_class.h:38
bool ConstraintNameExists(const char *conname, Oid namespaceid)
#define snprintf
Definition: port.h:225

References ConstraintNameExists(), get_relname_relid(), label, makeObjectName(), NAMEDATALEN, OidIsValid, pfree(), relname, snprintf, and strlcpy().

Referenced by ChooseIndexName(), generateSerialExtraStmts(), and ReindexRelationConcurrently().

◆ CompareOpclassOptions()

static bool CompareOpclassOptions ( Datum opts1,
Datum opts2,
int  natts 
)
static

Definition at line 349 of file indexcmds.c.

350 {
351  int i;
352 
353  if (!opts1 && !opts2)
354  return true;
355 
356  for (i = 0; i < natts; i++)
357  {
358  Datum opt1 = opts1 ? opts1[i] : (Datum) 0;
359  Datum opt2 = opts2 ? opts2[i] : (Datum) 0;
360 
361  if (opt1 == (Datum) 0)
362  {
363  if (opt2 == (Datum) 0)
364  continue;
365  else
366  return false;
367  }
368  else if (opt2 == (Datum) 0)
369  return false;
370 
371  /* Compare non-NULL text[] datums. */
372  if (!DatumGetBool(DirectFunctionCall2(array_eq, opt1, opt2)))
373  return false;
374  }
375 
376  return true;
377 }
Datum array_eq(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:3605
#define DirectFunctionCall2(func, arg1, arg2)
Definition: fmgr.h:633
#define DatumGetBool(X)
Definition: postgres.h:437

References array_eq(), DatumGetBool, DirectFunctionCall2, and i.

Referenced by CheckIndexCompatible().

◆ ComputeIndexAttrs()

static void ComputeIndexAttrs ( IndexInfo indexInfo,
Oid typeOidP,
Oid collationOidP,
Oid classOidP,
int16 colOptionP,
List attList,
List exclusionOpNames,
Oid  relId,
const char *  accessMethodName,
Oid  accessMethodId,
bool  amcanorder,
bool  isconstraint,
Oid  ddl_userid,
int  ddl_sec_context,
int *  ddl_save_nestlevel 
)
static

Definition at line 1752 of file indexcmds.c.

1767 {
1768  ListCell *nextExclOp;
1769  ListCell *lc;
1770  int attn;
1771  int nkeycols = indexInfo->ii_NumIndexKeyAttrs;
1772  Oid save_userid;
1773  int save_sec_context;
1774 
1775  /* Allocate space for exclusion operator info, if needed */
1776  if (exclusionOpNames)
1777  {
1778  Assert(list_length(exclusionOpNames) == nkeycols);
1779  indexInfo->ii_ExclusionOps = (Oid *) palloc(sizeof(Oid) * nkeycols);
1780  indexInfo->ii_ExclusionProcs = (Oid *) palloc(sizeof(Oid) * nkeycols);
1781  indexInfo->ii_ExclusionStrats = (uint16 *) palloc(sizeof(uint16) * nkeycols);
1782  nextExclOp = list_head(exclusionOpNames);
1783  }
1784  else
1785  nextExclOp = NULL;
1786 
1787  if (OidIsValid(ddl_userid))
1788  GetUserIdAndSecContext(&save_userid, &save_sec_context);
1789 
1790  /*
1791  * process attributeList
1792  */
1793  attn = 0;
1794  foreach(lc, attList)
1795  {
1796  IndexElem *attribute = (IndexElem *) lfirst(lc);
1797  Oid atttype;
1798  Oid attcollation;
1799 
1800  /*
1801  * Process the column-or-expression to be indexed.
1802  */
1803  if (attribute->name != NULL)
1804  {
1805  /* Simple index attribute */
1806  HeapTuple atttuple;
1807  Form_pg_attribute attform;
1808 
1809  Assert(attribute->expr == NULL);
1810  atttuple = SearchSysCacheAttName(relId, attribute->name);
1811  if (!HeapTupleIsValid(atttuple))
1812  {
1813  /* difference in error message spellings is historical */
1814  if (isconstraint)
1815  ereport(ERROR,
1816  (errcode(ERRCODE_UNDEFINED_COLUMN),
1817  errmsg("column \"%s\" named in key does not exist",
1818  attribute->name)));
1819  else
1820  ereport(ERROR,
1821  (errcode(ERRCODE_UNDEFINED_COLUMN),
1822  errmsg("column \"%s\" does not exist",
1823  attribute->name)));
1824  }
1825  attform = (Form_pg_attribute) GETSTRUCT(atttuple);
1826  indexInfo->ii_IndexAttrNumbers[attn] = attform->attnum;
1827  atttype = attform->atttypid;
1828  attcollation = attform->attcollation;
1829  ReleaseSysCache(atttuple);
1830  }
1831  else
1832  {
1833  /* Index expression */
1834  Node *expr = attribute->expr;
1835 
1836  Assert(expr != NULL);
1837 
1838  if (attn >= nkeycols)
1839  ereport(ERROR,
1840  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1841  errmsg("expressions are not supported in included columns")));
1842  atttype = exprType(expr);
1843  attcollation = exprCollation(expr);
1844 
1845  /*
1846  * Strip any top-level COLLATE clause. This ensures that we treat
1847  * "x COLLATE y" and "(x COLLATE y)" alike.
1848  */
1849  while (IsA(expr, CollateExpr))
1850  expr = (Node *) ((CollateExpr *) expr)->arg;
1851 
1852  if (IsA(expr, Var) &&
1853  ((Var *) expr)->varattno != InvalidAttrNumber)
1854  {
1855  /*
1856  * User wrote "(column)" or "(column COLLATE something)".
1857  * Treat it like simple attribute anyway.
1858  */
1859  indexInfo->ii_IndexAttrNumbers[attn] = ((Var *) expr)->varattno;
1860  }
1861  else
1862  {
1863  indexInfo->ii_IndexAttrNumbers[attn] = 0; /* marks expression */
1864  indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions,
1865  expr);
1866 
1867  /*
1868  * transformExpr() should have already rejected subqueries,
1869  * aggregates, and window functions, based on the EXPR_KIND_
1870  * for an index expression.
1871  */
1872 
1873  /*
1874  * An expression using mutable functions is probably wrong,
1875  * since if you aren't going to get the same result for the
1876  * same data every time, it's not clear what the index entries
1877  * mean at all.
1878  */
1879  if (CheckMutability((Expr *) expr))
1880  ereport(ERROR,
1881  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1882  errmsg("functions in index expression must be marked IMMUTABLE")));
1883  }
1884  }
1885 
1886  typeOidP[attn] = atttype;
1887 
1888  /*
1889  * Included columns have no collation, no opclass and no ordering
1890  * options.
1891  */
1892  if (attn >= nkeycols)
1893  {
1894  if (attribute->collation)
1895  ereport(ERROR,
1896  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1897  errmsg("including column does not support a collation")));
1898  if (attribute->opclass)
1899  ereport(ERROR,
1900  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1901  errmsg("including column does not support an operator class")));
1902  if (attribute->ordering != SORTBY_DEFAULT)
1903  ereport(ERROR,
1904  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1905  errmsg("including column does not support ASC/DESC options")));
1906  if (attribute->nulls_ordering != SORTBY_NULLS_DEFAULT)
1907  ereport(ERROR,
1908  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1909  errmsg("including column does not support NULLS FIRST/LAST options")));
1910 
1911  classOidP[attn] = InvalidOid;
1912  colOptionP[attn] = 0;
1913  collationOidP[attn] = InvalidOid;
1914  attn++;
1915 
1916  continue;
1917  }
1918 
1919  /*
1920  * Apply collation override if any. Use of ddl_userid is necessary
1921  * due to ACL checks therein, and it's safe because collations don't
1922  * contain opaque expressions (or non-opaque expressions).
1923  */
1924  if (attribute->collation)
1925  {
1926  if (OidIsValid(ddl_userid))
1927  {
1928  AtEOXact_GUC(false, *ddl_save_nestlevel);
1929  SetUserIdAndSecContext(ddl_userid, ddl_sec_context);
1930  }
1931  attcollation = get_collation_oid(attribute->collation, false);
1932  if (OidIsValid(ddl_userid))
1933  {
1934  SetUserIdAndSecContext(save_userid, save_sec_context);
1935  *ddl_save_nestlevel = NewGUCNestLevel();
1936  }
1937  }
1938 
1939  /*
1940  * Check we have a collation iff it's a collatable type. The only
1941  * expected failures here are (1) COLLATE applied to a noncollatable
1942  * type, or (2) index expression had an unresolved collation. But we
1943  * might as well code this to be a complete consistency check.
1944  */
1945  if (type_is_collatable(atttype))
1946  {
1947  if (!OidIsValid(attcollation))
1948  ereport(ERROR,
1949  (errcode(ERRCODE_INDETERMINATE_COLLATION),
1950  errmsg("could not determine which collation to use for index expression"),
1951  errhint("Use the COLLATE clause to set the collation explicitly.")));
1952  }
1953  else
1954  {
1955  if (OidIsValid(attcollation))
1956  ereport(ERROR,
1957  (errcode(ERRCODE_DATATYPE_MISMATCH),
1958  errmsg("collations are not supported by type %s",
1959  format_type_be(atttype))));
1960  }
1961 
1962  collationOidP[attn] = attcollation;
1963 
1964  /*
1965  * Identify the opclass to use. Use of ddl_userid is necessary due to
1966  * ACL checks therein. This is safe despite opclasses containing
1967  * opaque expressions (specifically, functions), because only
1968  * superusers can define opclasses.
1969  */
1970  if (OidIsValid(ddl_userid))
1971  {
1972  AtEOXact_GUC(false, *ddl_save_nestlevel);
1973  SetUserIdAndSecContext(ddl_userid, ddl_sec_context);
1974  }
1975  classOidP[attn] = ResolveOpClass(attribute->opclass,
1976  atttype,
1977  accessMethodName,
1978  accessMethodId);
1979  if (OidIsValid(ddl_userid))
1980  {
1981  SetUserIdAndSecContext(save_userid, save_sec_context);
1982  *ddl_save_nestlevel = NewGUCNestLevel();
1983  }
1984 
1985  /*
1986  * Identify the exclusion operator, if any.
1987  */
1988  if (nextExclOp)
1989  {
1990  List *opname = (List *) lfirst(nextExclOp);
1991  Oid opid;
1992  Oid opfamily;
1993  int strat;
1994 
1995  /*
1996  * Find the operator --- it must accept the column datatype
1997  * without runtime coercion (but binary compatibility is OK).
1998  * Operators contain opaque expressions (specifically, functions).
1999  * compatible_oper_opid() boils down to oper() and
2000  * IsBinaryCoercible(). PostgreSQL would have security problems
2001  * elsewhere if oper() started calling opaque expressions.
2002  */
2003  if (OidIsValid(ddl_userid))
2004  {
2005  AtEOXact_GUC(false, *ddl_save_nestlevel);
2006  SetUserIdAndSecContext(ddl_userid, ddl_sec_context);
2007  }
2008  opid = compatible_oper_opid(opname, atttype, atttype, false);
2009  if (OidIsValid(ddl_userid))
2010  {
2011  SetUserIdAndSecContext(save_userid, save_sec_context);
2012  *ddl_save_nestlevel = NewGUCNestLevel();
2013  }
2014 
2015  /*
2016  * Only allow commutative operators to be used in exclusion
2017  * constraints. If X conflicts with Y, but Y does not conflict
2018  * with X, bad things will happen.
2019  */
2020  if (get_commutator(opid) != opid)
2021  ereport(ERROR,
2022  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2023  errmsg("operator %s is not commutative",
2024  format_operator(opid)),
2025  errdetail("Only commutative operators can be used in exclusion constraints.")));
2026 
2027  /*
2028  * Operator must be a member of the right opfamily, too
2029  */
2030  opfamily = get_opclass_family(classOidP[attn]);
2031  strat = get_op_opfamily_strategy(opid, opfamily);
2032  if (strat == 0)
2033  {
2034  HeapTuple opftuple;
2035  Form_pg_opfamily opfform;
2036 
2037  /*
2038  * attribute->opclass might not explicitly name the opfamily,
2039  * so fetch the name of the selected opfamily for use in the
2040  * error message.
2041  */
2042  opftuple = SearchSysCache1(OPFAMILYOID,
2043  ObjectIdGetDatum(opfamily));
2044  if (!HeapTupleIsValid(opftuple))
2045  elog(ERROR, "cache lookup failed for opfamily %u",
2046  opfamily);
2047  opfform = (Form_pg_opfamily) GETSTRUCT(opftuple);
2048 
2049  ereport(ERROR,
2050  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2051  errmsg("operator %s is not a member of operator family \"%s\"",
2052  format_operator(opid),
2053  NameStr(opfform->opfname)),
2054  errdetail("The exclusion operator must be related to the index operator class for the constraint.")));
2055  }
2056 
2057  indexInfo->ii_ExclusionOps[attn] = opid;
2058  indexInfo->ii_ExclusionProcs[attn] = get_opcode(opid);
2059  indexInfo->ii_ExclusionStrats[attn] = strat;
2060  nextExclOp = lnext(exclusionOpNames, nextExclOp);
2061  }
2062 
2063  /*
2064  * Set up the per-column options (indoption field). For now, this is
2065  * zero for any un-ordered index, while ordered indexes have DESC and
2066  * NULLS FIRST/LAST options.
2067  */
2068  colOptionP[attn] = 0;
2069  if (amcanorder)
2070  {
2071  /* default ordering is ASC */
2072  if (attribute->ordering == SORTBY_DESC)
2073  colOptionP[attn] |= INDOPTION_DESC;
2074  /* default null ordering is LAST for ASC, FIRST for DESC */
2075  if (attribute->nulls_ordering == SORTBY_NULLS_DEFAULT)
2076  {
2077  if (attribute->ordering == SORTBY_DESC)
2078  colOptionP[attn] |= INDOPTION_NULLS_FIRST;
2079  }
2080  else if (attribute->nulls_ordering == SORTBY_NULLS_FIRST)
2081  colOptionP[attn] |= INDOPTION_NULLS_FIRST;
2082  }
2083  else
2084  {
2085  /* index AM does not support ordering */
2086  if (attribute->ordering != SORTBY_DEFAULT)
2087  ereport(ERROR,
2088  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2089  errmsg("access method \"%s\" does not support ASC/DESC options",
2090  accessMethodName)));
2091  if (attribute->nulls_ordering != SORTBY_NULLS_DEFAULT)
2092  ereport(ERROR,
2093  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2094  errmsg("access method \"%s\" does not support NULLS FIRST/LAST options",
2095  accessMethodName)));
2096  }
2097 
2098  /* Set up the per-column opclass options (attoptions field). */
2099  if (attribute->opclassopts)
2100  {
2101  Assert(attn < nkeycols);
2102 
2103  if (!indexInfo->ii_OpclassOptions)
2104  indexInfo->ii_OpclassOptions =
2105  palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
2106 
2107  indexInfo->ii_OpclassOptions[attn] =
2108  transformRelOptions((Datum) 0, attribute->opclassopts,
2109  NULL, NULL, false, false);
2110  }
2111 
2112  attn++;
2113  }
2114 }
#define InvalidAttrNumber
Definition: attnum.h:23
#define NameStr(name)
Definition: c.h:681
int errdetail(const char *fmt,...)
Definition: elog.c:1037
int errhint(const char *fmt,...)
Definition: elog.c:1151
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
int NewGUCNestLevel(void)
Definition: guc.c:6476
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:6490
Oid ResolveOpClass(List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
Definition: indexcmds.c:2123
Oid get_opclass_family(Oid opclass)
Definition: lsyscache.c:1193
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1266
int get_op_opfamily_strategy(Oid opno, Oid opfamily)
Definition: lsyscache.c:81
bool type_is_collatable(Oid typid)
Definition: lsyscache.c:3038
Oid get_commutator(Oid opno)
Definition: lsyscache.c:1490
void * palloc0(Size size)
Definition: mcxt.c:1099
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:603
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:610
Oid get_collation_oid(List *name, bool missing_ok)
Definition: namespace.c:3647
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:41
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:788
#define IsA(nodeptr, _type_)
Definition: nodes.h:624
Oid compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError)
Definition: parse_oper.c:499
@ SORTBY_NULLS_DEFAULT
Definition: parsenodes.h:60
@ SORTBY_NULLS_FIRST
Definition: parsenodes.h:61
@ SORTBY_DESC
Definition: parsenodes.h:54
@ SORTBY_DEFAULT
Definition: parsenodes.h:52
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:207
void * arg
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:322
FormData_pg_opfamily * Form_pg_opfamily
Definition: pg_opfamily.h:51
char * format_operator(Oid operator_oid)
Definition: regproc.c:852
Datum transformRelOptions(Datum oldOptions, List *defList, const char *namspace, char *validnsps[], bool acceptOidsOff, bool isReset)
Definition: reloptions.c:1158
Node * expr
Definition: parsenodes.h:734
SortByDir ordering
Definition: parsenodes.h:739
List * opclassopts
Definition: parsenodes.h:738
SortByNulls nulls_ordering
Definition: parsenodes.h:740
List * opclass
Definition: parsenodes.h:737
List * collation
Definition: parsenodes.h:736
uint16 * ii_ExclusionStrats
Definition: execnodes.h:170
int ii_NumIndexAttrs
Definition: execnodes.h:161
int ii_NumIndexKeyAttrs
Definition: execnodes.h:162
List * ii_Expressions
Definition: execnodes.h:164
Oid * ii_ExclusionProcs
Definition: execnodes.h:169
AttrNumber ii_IndexAttrNumbers[INDEX_MAX_KEYS]
Definition: execnodes.h:163
Definition: primnodes.h:196
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
Definition: syscache.c:1314
@ OPFAMILYOID
Definition: syscache.h:74

References arg, Assert(), AtEOXact_GUC(), CheckMutability(), IndexElem::collation, compatible_oper_opid(), elog, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, IndexElem::expr, exprCollation(), exprType(), format_operator(), format_type_be(), get_collation_oid(), get_commutator(), get_op_opfamily_strategy(), get_opclass_family(), get_opcode(), GETSTRUCT, GetUserIdAndSecContext(), HeapTupleIsValid, IndexInfo::ii_ExclusionOps, IndexInfo::ii_ExclusionProcs, IndexInfo::ii_ExclusionStrats, IndexInfo::ii_Expressions, IndexInfo::ii_IndexAttrNumbers, IndexInfo::ii_NumIndexAttrs, IndexInfo::ii_NumIndexKeyAttrs, IndexInfo::ii_OpclassOptions, InvalidAttrNumber, InvalidOid, IsA, lappend(), lfirst, list_head(), list_length(), lnext(), IndexElem::name, NameStr, NewGUCNestLevel(), IndexElem::nulls_ordering, ObjectIdGetDatum, OidIsValid, IndexElem::opclass, IndexElem::opclassopts, OPFAMILYOID, IndexElem::ordering, palloc(), palloc0(), ReleaseSysCache(), ResolveOpClass(), SearchSysCache1(), SearchSysCacheAttName(), SetUserIdAndSecContext(), SORTBY_DEFAULT, SORTBY_DESC, SORTBY_NULLS_DEFAULT, SORTBY_NULLS_FIRST, transformRelOptions(), and type_is_collatable().

Referenced by CheckIndexCompatible(), and DefineIndex().

◆ DefineIndex()

ObjectAddress DefineIndex ( Oid  relationId,
IndexStmt stmt,
Oid  indexRelationId,
Oid  parentIndexId,
Oid  parentConstraintId,
bool  is_alter_table,
bool  check_rights,
bool  check_not_in_use,
bool  skip_build,
bool  quiet 
)

Definition at line 521 of file indexcmds.c.

531 {
532  bool concurrent;
533  char *indexRelationName;
534  char *accessMethodName;
535  Oid *typeObjectId;
536  Oid *collationObjectId;
537  Oid *classObjectId;
538  Oid accessMethodId;
539  Oid namespaceId;
540  Oid tablespaceId;
541  Oid createdConstraintId = InvalidOid;
542  List *indexColNames;
543  List *allIndexParams;
544  Relation rel;
545  HeapTuple tuple;
546  Form_pg_am accessMethodForm;
547  IndexAmRoutine *amRoutine;
548  bool amcanorder;
549  amoptions_function amoptions;
550  bool partitioned;
551  bool safe_index;
552  Datum reloptions;
553  int16 *coloptions;
554  IndexInfo *indexInfo;
555  bits16 flags;
556  bits16 constr_flags;
557  int numberOfAttributes;
558  int numberOfKeyAttributes;
559  TransactionId limitXmin;
560  ObjectAddress address;
561  LockRelId heaprelid;
562  LOCKTAG heaplocktag;
563  LOCKMODE lockmode;
564  Snapshot snapshot;
565  Oid root_save_userid;
566  int root_save_sec_context;
567  int root_save_nestlevel;
568  int i;
569 
570  root_save_nestlevel = NewGUCNestLevel();
571 
572  /*
573  * Some callers need us to run with an empty default_tablespace; this is a
574  * necessary hack to be able to reproduce catalog state accurately when
575  * recreating indexes after table-rewriting ALTER TABLE.
576  */
577  if (stmt->reset_default_tblspc)
578  (void) set_config_option("default_tablespace", "",
580  GUC_ACTION_SAVE, true, 0, false);
581 
582  /*
583  * Force non-concurrent build on temporary relations, even if CONCURRENTLY
584  * was requested. Other backends can't access a temporary relation, so
585  * there's no harm in grabbing a stronger lock, and a non-concurrent DROP
586  * is more efficient. Do this before any use of the concurrent option is
587  * done.
588  */
589  if (stmt->concurrent && get_rel_persistence(relationId) != RELPERSISTENCE_TEMP)
590  concurrent = true;
591  else
592  concurrent = false;
593 
594  /*
595  * Start progress report. If we're building a partition, this was already
596  * done.
597  */
598  if (!OidIsValid(parentIndexId))
599  {
601  relationId);
603  concurrent ?
606  }
607 
608  /*
609  * No index OID to report yet
610  */
612  InvalidOid);
613 
614  /*
615  * count key attributes in index
616  */
617  numberOfKeyAttributes = list_length(stmt->indexParams);
618 
619  /*
620  * Calculate the new list of index columns including both key columns and
621  * INCLUDE columns. Later we can determine which of these are key
622  * columns, and which are just part of the INCLUDE list by checking the
623  * list position. A list item in a position less than ii_NumIndexKeyAttrs
624  * is part of the key columns, and anything equal to and over is part of
625  * the INCLUDE columns.
626  */
627  allIndexParams = list_concat_copy(stmt->indexParams,
628  stmt->indexIncludingParams);
629  numberOfAttributes = list_length(allIndexParams);
630 
631  if (numberOfKeyAttributes <= 0)
632  ereport(ERROR,
633  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
634  errmsg("must specify at least one column")));
635  if (numberOfAttributes > INDEX_MAX_KEYS)
636  ereport(ERROR,
637  (errcode(ERRCODE_TOO_MANY_COLUMNS),
638  errmsg("cannot use more than %d columns in an index",
639  INDEX_MAX_KEYS)));
640 
641  /*
642  * Only SELECT ... FOR UPDATE/SHARE are allowed while doing a standard
643  * index build; but for concurrent builds we allow INSERT/UPDATE/DELETE
644  * (but not VACUUM).
645  *
646  * NB: Caller is responsible for making sure that relationId refers to the
647  * relation on which the index should be built; except in bootstrap mode,
648  * this will typically require the caller to have already locked the
649  * relation. To avoid lock upgrade hazards, that lock should be at least
650  * as strong as the one we take here.
651  *
652  * NB: If the lock strength here ever changes, code that is run by
653  * parallel workers under the control of certain particular ambuild
654  * functions will need to be updated, too.
655  */
656  lockmode = concurrent ? ShareUpdateExclusiveLock : ShareLock;
657  rel = table_open(relationId, lockmode);
658 
659  /*
660  * Switch to the table owner's userid, so that any index functions are run
661  * as that user. Also lock down security-restricted operations. We
662  * already arranged to make GUC variable changes local to this command.
663  */
664  GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context);
665  SetUserIdAndSecContext(rel->rd_rel->relowner,
666  root_save_sec_context | SECURITY_RESTRICTED_OPERATION);
667 
668  namespaceId = RelationGetNamespace(rel);
669 
670  /* Ensure that it makes sense to index this kind of relation */
671  switch (rel->rd_rel->relkind)
672  {
673  case RELKIND_RELATION:
674  case RELKIND_MATVIEW:
675  case RELKIND_PARTITIONED_TABLE:
676  /* OK */
677  break;
678  default:
679  ereport(ERROR,
680  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
681  errmsg("cannot create index on relation \"%s\"",
683  errdetail_relkind_not_supported(rel->rd_rel->relkind)));
684  break;
685  }
686 
687  /*
688  * Establish behavior for partitioned tables, and verify sanity of
689  * parameters.
690  *
691  * We do not build an actual index in this case; we only create a few
692  * catalog entries. The actual indexes are built by recursing for each
693  * partition.
694  */
695  partitioned = rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE;
696  if (partitioned)
697  {
698  /*
699  * Note: we check 'stmt->concurrent' rather than 'concurrent', so that
700  * the error is thrown also for temporary tables. Seems better to be
701  * consistent, even though we could do it on temporary table because
702  * we're not actually doing it concurrently.
703  */
704  if (stmt->concurrent)
705  ereport(ERROR,
706  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
707  errmsg("cannot create index on partitioned table \"%s\" concurrently",
708  RelationGetRelationName(rel))));
709  if (stmt->excludeOpNames)
710  ereport(ERROR,
711  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
712  errmsg("cannot create exclusion constraints on partitioned table \"%s\"",
713  RelationGetRelationName(rel))));
714  }
715 
716  /*
717  * Don't try to CREATE INDEX on temp tables of other backends.
718  */
719  if (RELATION_IS_OTHER_TEMP(rel))
720  ereport(ERROR,
721  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
722  errmsg("cannot create indexes on temporary tables of other sessions")));
723 
724  /*
725  * Unless our caller vouches for having checked this already, insist that
726  * the table not be in use by our own session, either. Otherwise we might
727  * fail to make entries in the new index (for instance, if an INSERT or
728  * UPDATE is in progress and has already made its list of target indexes).
729  */
730  if (check_not_in_use)
731  CheckTableNotInUse(rel, "CREATE INDEX");
732 
733  /*
734  * Verify we (still) have CREATE rights in the rel's namespace.
735  * (Presumably we did when the rel was created, but maybe not anymore.)
736  * Skip check if caller doesn't want it. Also skip check if
737  * bootstrapping, since permissions machinery may not be working yet.
738  */
739  if (check_rights && !IsBootstrapProcessingMode())
740  {
741  AclResult aclresult;
742 
743  aclresult = pg_namespace_aclcheck(namespaceId, root_save_userid,
744  ACL_CREATE);
745  if (aclresult != ACLCHECK_OK)
746  aclcheck_error(aclresult, OBJECT_SCHEMA,
747  get_namespace_name(namespaceId));
748  }
749 
750  /*
751  * Select tablespace to use. If not specified, use default tablespace
752  * (which may in turn default to database's default).
753  */
754  if (stmt->tableSpace)
755  {
756  tablespaceId = get_tablespace_oid(stmt->tableSpace, false);
757  if (partitioned && tablespaceId == MyDatabaseTableSpace)
758  ereport(ERROR,
759  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
760  errmsg("cannot specify default tablespace for partitioned relations")));
761  }
762  else
763  {
764  tablespaceId = GetDefaultTablespace(rel->rd_rel->relpersistence,
765  partitioned);
766  /* note InvalidOid is OK in this case */
767  }
768 
769  /* Check tablespace permissions */
770  if (check_rights &&
771  OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
772  {
773  AclResult aclresult;
774 
775  aclresult = pg_tablespace_aclcheck(tablespaceId, root_save_userid,
776  ACL_CREATE);
777  if (aclresult != ACLCHECK_OK)
779  get_tablespace_name(tablespaceId));
780  }
781 
782  /*
783  * Force shared indexes into the pg_global tablespace. This is a bit of a
784  * hack but seems simpler than marking them in the BKI commands. On the
785  * other hand, if it's not shared, don't allow it to be placed there.
786  */
787  if (rel->rd_rel->relisshared)
788  tablespaceId = GLOBALTABLESPACE_OID;
789  else if (tablespaceId == GLOBALTABLESPACE_OID)
790  ereport(ERROR,
791  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
792  errmsg("only shared relations can be placed in pg_global tablespace")));
793 
794  /*
795  * Choose the index column names.
796  */
797  indexColNames = ChooseIndexColumnNames(allIndexParams);
798 
799  /*
800  * Select name for index if caller didn't specify
801  */
802  indexRelationName = stmt->idxname;
803  if (indexRelationName == NULL)
804  indexRelationName = ChooseIndexName(RelationGetRelationName(rel),
805  namespaceId,
806  indexColNames,
807  stmt->excludeOpNames,
808  stmt->primary,
809  stmt->isconstraint);
810 
811  /*
812  * look up the access method, verify it can handle the requested features
813  */
814  accessMethodName = stmt->accessMethod;
815  tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
816  if (!HeapTupleIsValid(tuple))
817  {
818  /*
819  * Hack to provide more-or-less-transparent updating of old RTREE
820  * indexes to GiST: if RTREE is requested and not found, use GIST.
821  */
822  if (strcmp(accessMethodName, "rtree") == 0)
823  {
824  ereport(NOTICE,
825  (errmsg("substituting access method \"gist\" for obsolete method \"rtree\"")));
826  accessMethodName = "gist";
827  tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
828  }
829 
830  if (!HeapTupleIsValid(tuple))
831  ereport(ERROR,
832  (errcode(ERRCODE_UNDEFINED_OBJECT),
833  errmsg("access method \"%s\" does not exist",
834  accessMethodName)));
835  }
836  accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
837  accessMethodId = accessMethodForm->oid;
838  amRoutine = GetIndexAmRoutine(accessMethodForm->amhandler);
839 
841  accessMethodId);
842 
843  if (stmt->unique && !amRoutine->amcanunique)
844  ereport(ERROR,
845  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
846  errmsg("access method \"%s\" does not support unique indexes",
847  accessMethodName)));
848  if (stmt->indexIncludingParams != NIL && !amRoutine->amcaninclude)
849  ereport(ERROR,
850  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
851  errmsg("access method \"%s\" does not support included columns",
852  accessMethodName)));
853  if (numberOfKeyAttributes > 1 && !amRoutine->amcanmulticol)
854  ereport(ERROR,
855  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
856  errmsg("access method \"%s\" does not support multicolumn indexes",
857  accessMethodName)));
858  if (stmt->excludeOpNames && amRoutine->amgettuple == NULL)
859  ereport(ERROR,
860  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
861  errmsg("access method \"%s\" does not support exclusion constraints",
862  accessMethodName)));
863 
864  amcanorder = amRoutine->amcanorder;
865  amoptions = amRoutine->amoptions;
866 
867  pfree(amRoutine);
868  ReleaseSysCache(tuple);
869 
870  /*
871  * Validate predicate, if given
872  */
873  if (stmt->whereClause)
874  CheckPredicate((Expr *) stmt->whereClause);
875 
876  /*
877  * Parse AM-specific options, convert to text array form, validate.
878  */
879  reloptions = transformRelOptions((Datum) 0, stmt->options,
880  NULL, NULL, false, false);
881 
882  (void) index_reloptions(amoptions, reloptions, true);
883 
884  /*
885  * Prepare arguments for index_create, primarily an IndexInfo structure.
886  * Note that predicates must be in implicit-AND format. In a concurrent
887  * build, mark it not-ready-for-inserts.
888  */
889  indexInfo = makeIndexInfo(numberOfAttributes,
890  numberOfKeyAttributes,
891  accessMethodId,
892  NIL, /* expressions, NIL for now */
894  stmt->unique,
895  stmt->nulls_not_distinct,
896  !concurrent,
897  concurrent);
898 
899  typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
900  collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
901  classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
902  coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
903  ComputeIndexAttrs(indexInfo,
904  typeObjectId, collationObjectId, classObjectId,
905  coloptions, allIndexParams,
906  stmt->excludeOpNames, relationId,
907  accessMethodName, accessMethodId,
908  amcanorder, stmt->isconstraint, root_save_userid,
909  root_save_sec_context, &root_save_nestlevel);
910 
911  /*
912  * Extra checks when creating a PRIMARY KEY index.
913  */
914  if (stmt->primary)
915  index_check_primary_key(rel, indexInfo, is_alter_table, stmt);
916 
917  /*
918  * If this table is partitioned and we're creating a unique index or a
919  * primary key, make sure that the partition key is a subset of the
920  * index's columns. Otherwise it would be possible to violate uniqueness
921  * by putting values that ought to be unique in different partitions.
922  *
923  * We could lift this limitation if we had global indexes, but those have
924  * their own problems, so this is a useful feature combination.
925  */
926  if (partitioned && (stmt->unique || stmt->primary))
927  {
929  const char *constraint_type;
930  int i;
931 
932  if (stmt->primary)
933  constraint_type = "PRIMARY KEY";
934  else if (stmt->unique)
935  constraint_type = "UNIQUE";
936  else if (stmt->excludeOpNames != NIL)
937  constraint_type = "EXCLUDE";
938  else
939  {
940  elog(ERROR, "unknown constraint type");
941  constraint_type = NULL; /* keep compiler quiet */
942  }
943 
944  /*
945  * Verify that all the columns in the partition key appear in the
946  * unique key definition, with the same notion of equality.
947  */
948  for (i = 0; i < key->partnatts; i++)
949  {
950  bool found = false;
951  int eq_strategy;
952  Oid ptkey_eqop;
953  int j;
954 
955  /*
956  * Identify the equality operator associated with this partkey
957  * column. For list and range partitioning, partkeys use btree
958  * operator classes; hash partitioning uses hash operator classes.
959  * (Keep this in sync with ComputePartitionAttrs!)
960  */
961  if (key->strategy == PARTITION_STRATEGY_HASH)
962  eq_strategy = HTEqualStrategyNumber;
963  else
964  eq_strategy = BTEqualStrategyNumber;
965 
966  ptkey_eqop = get_opfamily_member(key->partopfamily[i],
967  key->partopcintype[i],
968  key->partopcintype[i],
969  eq_strategy);
970  if (!OidIsValid(ptkey_eqop))
971  elog(ERROR, "missing operator %d(%u,%u) in partition opfamily %u",
972  eq_strategy, key->partopcintype[i], key->partopcintype[i],
973  key->partopfamily[i]);
974 
975  /*
976  * We'll need to be able to identify the equality operators
977  * associated with index columns, too. We know what to do with
978  * btree opclasses; if there are ever any other index types that
979  * support unique indexes, this logic will need extension.
980  */
981  if (accessMethodId == BTREE_AM_OID)
982  eq_strategy = BTEqualStrategyNumber;
983  else
984  ereport(ERROR,
985  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
986  errmsg("cannot match partition key to an index using access method \"%s\"",
987  accessMethodName)));
988 
989  /*
990  * It may be possible to support UNIQUE constraints when partition
991  * keys are expressions, but is it worth it? Give up for now.
992  */
993  if (key->partattrs[i] == 0)
994  ereport(ERROR,
995  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
996  errmsg("unsupported %s constraint with partition key definition",
997  constraint_type),
998  errdetail("%s constraints cannot be used when partition keys include expressions.",
999  constraint_type)));
1000 
1001  /* Search the index column(s) for a match */
1002  for (j = 0; j < indexInfo->ii_NumIndexKeyAttrs; j++)
1003  {
1004  if (key->partattrs[i] == indexInfo->ii_IndexAttrNumbers[j])
1005  {
1006  /* Matched the column, now what about the equality op? */
1007  Oid idx_opfamily;
1008  Oid idx_opcintype;
1009 
1010  if (get_opclass_opfamily_and_input_type(classObjectId[j],
1011  &idx_opfamily,
1012  &idx_opcintype))
1013  {
1014  Oid idx_eqop;
1015 
1016  idx_eqop = get_opfamily_member(idx_opfamily,
1017  idx_opcintype,
1018  idx_opcintype,
1019  eq_strategy);
1020  if (ptkey_eqop == idx_eqop)
1021  {
1022  found = true;
1023  break;
1024  }
1025  }
1026  }
1027  }
1028 
1029  if (!found)
1030  {
1031  Form_pg_attribute att;
1032 
1033  att = TupleDescAttr(RelationGetDescr(rel),
1034  key->partattrs[i] - 1);
1035  ereport(ERROR,
1036  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1037  errmsg("unique constraint on partitioned table must include all partitioning columns"),
1038  errdetail("%s constraint on table \"%s\" lacks column \"%s\" which is part of the partition key.",
1039  constraint_type, RelationGetRelationName(rel),
1040  NameStr(att->attname))));
1041  }
1042  }
1043  }
1044 
1045 
1046  /*
1047  * We disallow indexes on system columns. They would not necessarily get
1048  * updated correctly, and they don't seem useful anyway.
1049  */
1050  for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
1051  {
1052  AttrNumber attno = indexInfo->ii_IndexAttrNumbers[i];
1053 
1054  if (attno < 0)
1055  ereport(ERROR,
1056  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1057  errmsg("index creation on system columns is not supported")));
1058  }
1059 
1060  /*
1061  * Also check for system columns used in expressions or predicates.
1062  */
1063  if (indexInfo->ii_Expressions || indexInfo->ii_Predicate)
1064  {
1065  Bitmapset *indexattrs = NULL;
1066 
1067  pull_varattnos((Node *) indexInfo->ii_Expressions, 1, &indexattrs);
1068  pull_varattnos((Node *) indexInfo->ii_Predicate, 1, &indexattrs);
1069 
1070  for (i = FirstLowInvalidHeapAttributeNumber + 1; i < 0; i++)
1071  {
1073  indexattrs))
1074  ereport(ERROR,
1075  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1076  errmsg("index creation on system columns is not supported")));
1077  }
1078  }
1079 
1080  /* Is index safe for others to ignore? See set_indexsafe_procflags() */
1081  safe_index = indexInfo->ii_Expressions == NIL &&
1082  indexInfo->ii_Predicate == NIL;
1083 
1084  /*
1085  * Report index creation if appropriate (delay this till after most of the
1086  * error checks)
1087  */
1088  if (stmt->isconstraint && !quiet)
1089  {
1090  const char *constraint_type;
1091 
1092  if (stmt->primary)
1093  constraint_type = "PRIMARY KEY";
1094  else if (stmt->unique)
1095  constraint_type = "UNIQUE";
1096  else if (stmt->excludeOpNames != NIL)
1097  constraint_type = "EXCLUDE";
1098  else
1099  {
1100  elog(ERROR, "unknown constraint type");
1101  constraint_type = NULL; /* keep compiler quiet */
1102  }
1103 
1104  ereport(DEBUG1,
1105  (errmsg_internal("%s %s will create implicit index \"%s\" for table \"%s\"",
1106  is_alter_table ? "ALTER TABLE / ADD" : "CREATE TABLE /",
1107  constraint_type,
1108  indexRelationName, RelationGetRelationName(rel))));
1109  }
1110 
1111  /*
1112  * A valid stmt->oldNode implies that we already have a built form of the
1113  * index. The caller should also decline any index build.
1114  */
1115  Assert(!OidIsValid(stmt->oldNode) || (skip_build && !concurrent));
1116 
1117  /*
1118  * Make the catalog entries for the index, including constraints. This
1119  * step also actually builds the index, except if caller requested not to
1120  * or in concurrent mode, in which case it'll be done later, or doing a
1121  * partitioned index (because those don't have storage).
1122  */
1123  flags = constr_flags = 0;
1124  if (stmt->isconstraint)
1125  flags |= INDEX_CREATE_ADD_CONSTRAINT;
1126  if (skip_build || concurrent || partitioned)
1127  flags |= INDEX_CREATE_SKIP_BUILD;
1128  if (stmt->if_not_exists)
1129  flags |= INDEX_CREATE_IF_NOT_EXISTS;
1130  if (concurrent)
1131  flags |= INDEX_CREATE_CONCURRENT;
1132  if (partitioned)
1133  flags |= INDEX_CREATE_PARTITIONED;
1134  if (stmt->primary)
1135  flags |= INDEX_CREATE_IS_PRIMARY;
1136 
1137  /*
1138  * If the table is partitioned, and recursion was declined but partitions
1139  * exist, mark the index as invalid.
1140  */
1141  if (partitioned && stmt->relation && !stmt->relation->inh)
1142  {
1143  PartitionDesc pd = RelationGetPartitionDesc(rel, true);
1144 
1145  if (pd->nparts != 0)
1146  flags |= INDEX_CREATE_INVALID;
1147  }
1148 
1149  if (stmt->deferrable)
1150  constr_flags |= INDEX_CONSTR_CREATE_DEFERRABLE;
1151  if (stmt->initdeferred)
1152  constr_flags |= INDEX_CONSTR_CREATE_INIT_DEFERRED;
1153 
1154  indexRelationId =
1155  index_create(rel, indexRelationName, indexRelationId, parentIndexId,
1156  parentConstraintId,
1157  stmt->oldNode, indexInfo, indexColNames,
1158  accessMethodId, tablespaceId,
1159  collationObjectId, classObjectId,
1160  coloptions, reloptions,
1161  flags, constr_flags,
1162  allowSystemTableMods, !check_rights,
1163  &createdConstraintId);
1164 
1165  ObjectAddressSet(address, RelationRelationId, indexRelationId);
1166 
1167  if (!OidIsValid(indexRelationId))
1168  {
1169  /*
1170  * Roll back any GUC changes executed by index functions. Also revert
1171  * to original default_tablespace if we changed it above.
1172  */
1173  AtEOXact_GUC(false, root_save_nestlevel);
1174 
1175  /* Restore userid and security context */
1176  SetUserIdAndSecContext(root_save_userid, root_save_sec_context);
1177 
1178  table_close(rel, NoLock);
1179 
1180  /* If this is the top-level index, we're done */
1181  if (!OidIsValid(parentIndexId))
1183 
1184  return address;
1185  }
1186 
1187  /*
1188  * Roll back any GUC changes executed by index functions, and keep
1189  * subsequent changes local to this command. This is essential if some
1190  * index function changed a behavior-affecting GUC, e.g. search_path.
1191  */
1192  AtEOXact_GUC(false, root_save_nestlevel);
1193  root_save_nestlevel = NewGUCNestLevel();
1194 
1195  /* Add any requested comment */
1196  if (stmt->idxcomment != NULL)
1197  CreateComments(indexRelationId, RelationRelationId, 0,
1198  stmt->idxcomment);
1199 
1200  if (partitioned)
1201  {
1202  PartitionDesc partdesc;
1203 
1204  /*
1205  * Unless caller specified to skip this step (via ONLY), process each
1206  * partition to make sure they all contain a corresponding index.
1207  *
1208  * If we're called internally (no stmt->relation), recurse always.
1209  */
1210  partdesc = RelationGetPartitionDesc(rel, true);
1211  if ((!stmt->relation || stmt->relation->inh) && partdesc->nparts > 0)
1212  {
1213  int nparts = partdesc->nparts;
1214  Oid *part_oids = palloc(sizeof(Oid) * nparts);
1215  bool invalidate_parent = false;
1216  TupleDesc parentDesc;
1217  Oid *opfamOids;
1218 
1220  nparts);
1221 
1222  memcpy(part_oids, partdesc->oids, sizeof(Oid) * nparts);
1223 
1224  parentDesc = RelationGetDescr(rel);
1225  opfamOids = palloc(sizeof(Oid) * numberOfKeyAttributes);
1226  for (i = 0; i < numberOfKeyAttributes; i++)
1227  opfamOids[i] = get_opclass_family(classObjectId[i]);
1228 
1229  /*
1230  * For each partition, scan all existing indexes; if one matches
1231  * our index definition and is not already attached to some other
1232  * parent index, attach it to the one we just created.
1233  *
1234  * If none matches, build a new index by calling ourselves
1235  * recursively with the same options (except for the index name).
1236  */
1237  for (i = 0; i < nparts; i++)
1238  {
1239  Oid childRelid = part_oids[i];
1240  Relation childrel;
1241  Oid child_save_userid;
1242  int child_save_sec_context;
1243  int child_save_nestlevel;
1244  List *childidxs;
1245  ListCell *cell;
1246  AttrMap *attmap;
1247  bool found = false;
1248 
1249  childrel = table_open(childRelid, lockmode);
1250 
1251  GetUserIdAndSecContext(&child_save_userid,
1252  &child_save_sec_context);
1253  SetUserIdAndSecContext(childrel->rd_rel->relowner,
1254  child_save_sec_context | SECURITY_RESTRICTED_OPERATION);
1255  child_save_nestlevel = NewGUCNestLevel();
1256 
1257  /*
1258  * Don't try to create indexes on foreign tables, though. Skip
1259  * those if a regular index, or fail if trying to create a
1260  * constraint index.
1261  */
1262  if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1263  {
1264  if (stmt->unique || stmt->primary)
1265  ereport(ERROR,
1266  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1267  errmsg("cannot create unique index on partitioned table \"%s\"",
1269  errdetail("Table \"%s\" contains partitions that are foreign tables.",
1270  RelationGetRelationName(rel))));
1271 
1272  AtEOXact_GUC(false, child_save_nestlevel);
1273  SetUserIdAndSecContext(child_save_userid,
1274  child_save_sec_context);
1275  table_close(childrel, lockmode);
1276  continue;
1277  }
1278 
1279  childidxs = RelationGetIndexList(childrel);
1280  attmap =
1282  parentDesc);
1283 
1284  foreach(cell, childidxs)
1285  {
1286  Oid cldidxid = lfirst_oid(cell);
1287  Relation cldidx;
1288  IndexInfo *cldIdxInfo;
1289 
1290  /* this index is already partition of another one */
1291  if (has_superclass(cldidxid))
1292  continue;
1293 
1294  cldidx = index_open(cldidxid, lockmode);
1295  cldIdxInfo = BuildIndexInfo(cldidx);
1296  if (CompareIndexInfo(cldIdxInfo, indexInfo,
1297  cldidx->rd_indcollation,
1298  collationObjectId,
1299  cldidx->rd_opfamily,
1300  opfamOids,
1301  attmap))
1302  {
1303  Oid cldConstrOid = InvalidOid;
1304 
1305  /*
1306  * Found a match.
1307  *
1308  * If this index is being created in the parent
1309  * because of a constraint, then the child needs to
1310  * have a constraint also, so look for one. If there
1311  * is no such constraint, this index is no good, so
1312  * keep looking.
1313  */
1314  if (createdConstraintId != InvalidOid)
1315  {
1316  cldConstrOid =
1318  cldidxid);
1319  if (cldConstrOid == InvalidOid)
1320  {
1321  index_close(cldidx, lockmode);
1322  continue;
1323  }
1324  }
1325 
1326  /* Attach index to parent and we're done. */
1327  IndexSetParentIndex(cldidx, indexRelationId);
1328  if (createdConstraintId != InvalidOid)
1329  ConstraintSetParentConstraint(cldConstrOid,
1330  createdConstraintId,
1331  childRelid);
1332 
1333  if (!cldidx->rd_index->indisvalid)
1334  invalidate_parent = true;
1335 
1336  found = true;
1337  /* keep lock till commit */
1338  index_close(cldidx, NoLock);
1339  break;
1340  }
1341 
1342  index_close(cldidx, lockmode);
1343  }
1344 
1345  list_free(childidxs);
1346  AtEOXact_GUC(false, child_save_nestlevel);
1347  SetUserIdAndSecContext(child_save_userid,
1348  child_save_sec_context);
1349  table_close(childrel, NoLock);
1350 
1351  /*
1352  * If no matching index was found, create our own.
1353  */
1354  if (!found)
1355  {
1356  IndexStmt *childStmt = copyObject(stmt);
1357  bool found_whole_row;
1358  ListCell *lc;
1359 
1360  /*
1361  * We can't use the same index name for the child index,
1362  * so clear idxname to let the recursive invocation choose
1363  * a new name. Likewise, the existing target relation
1364  * field is wrong, and if indexOid or oldNode are set,
1365  * they mustn't be applied to the child either.
1366  */
1367  childStmt->idxname = NULL;
1368  childStmt->relation = NULL;
1369  childStmt->indexOid = InvalidOid;
1370  childStmt->oldNode = InvalidOid;
1373 
1374  /*
1375  * Adjust any Vars (both in expressions and in the index's
1376  * WHERE clause) to match the partition's column numbering
1377  * in case it's different from the parent's.
1378  */
1379  foreach(lc, childStmt->indexParams)
1380  {
1381  IndexElem *ielem = lfirst(lc);
1382 
1383  /*
1384  * If the index parameter is an expression, we must
1385  * translate it to contain child Vars.
1386  */
1387  if (ielem->expr)
1388  {
1389  ielem->expr =
1390  map_variable_attnos((Node *) ielem->expr,
1391  1, 0, attmap,
1392  InvalidOid,
1393  &found_whole_row);
1394  if (found_whole_row)
1395  elog(ERROR, "cannot convert whole-row table reference");
1396  }
1397  }
1398  childStmt->whereClause =
1399  map_variable_attnos(stmt->whereClause, 1, 0,
1400  attmap,
1401  InvalidOid, &found_whole_row);
1402  if (found_whole_row)
1403  elog(ERROR, "cannot convert whole-row table reference");
1404 
1405  /*
1406  * Recurse as the starting user ID. Callee will use that
1407  * for permission checks, then switch again.
1408  */
1409  Assert(GetUserId() == child_save_userid);
1410  SetUserIdAndSecContext(root_save_userid,
1411  root_save_sec_context);
1412  DefineIndex(childRelid, childStmt,
1413  InvalidOid, /* no predefined OID */
1414  indexRelationId, /* this is our child */
1415  createdConstraintId,
1416  is_alter_table, check_rights, check_not_in_use,
1417  skip_build, quiet);
1418  SetUserIdAndSecContext(child_save_userid,
1419  child_save_sec_context);
1420  }
1421 
1423  i + 1);
1424  free_attrmap(attmap);
1425  }
1426 
1427  /*
1428  * The pg_index row we inserted for this index was marked
1429  * indisvalid=true. But if we attached an existing index that is
1430  * invalid, this is incorrect, so update our row to invalid too.
1431  */
1432  if (invalidate_parent)
1433  {
1434  Relation pg_index = table_open(IndexRelationId, RowExclusiveLock);
1435  HeapTuple tup,
1436  newtup;
1437 
1439  ObjectIdGetDatum(indexRelationId));
1440  if (!HeapTupleIsValid(tup))
1441  elog(ERROR, "cache lookup failed for index %u",
1442  indexRelationId);
1443  newtup = heap_copytuple(tup);
1444  ((Form_pg_index) GETSTRUCT(newtup))->indisvalid = false;
1445  CatalogTupleUpdate(pg_index, &tup->t_self, newtup);
1446  ReleaseSysCache(tup);
1447  table_close(pg_index, RowExclusiveLock);
1448  heap_freetuple(newtup);
1449  }
1450  }
1451 
1452  /*
1453  * Indexes on partitioned tables are not themselves built, so we're
1454  * done here.
1455  */
1456  AtEOXact_GUC(false, root_save_nestlevel);
1457  SetUserIdAndSecContext(root_save_userid, root_save_sec_context);
1458  table_close(rel, NoLock);
1459  if (!OidIsValid(parentIndexId))
1461  return address;
1462  }
1463 
1464  AtEOXact_GUC(false, root_save_nestlevel);
1465  SetUserIdAndSecContext(root_save_userid, root_save_sec_context);
1466 
1467  if (!concurrent)
1468  {
1469  /* Close the heap and we're done, in the non-concurrent case */
1470  table_close(rel, NoLock);
1471 
1472  /* If this is the top-level index, we're done. */
1473  if (!OidIsValid(parentIndexId))
1475 
1476  return address;
1477  }
1478 
1479  /* save lockrelid and locktag for below, then close rel */
1480  heaprelid = rel->rd_lockInfo.lockRelId;
1481  SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId);
1482  table_close(rel, NoLock);
1483 
1484  /*
1485  * For a concurrent build, it's important to make the catalog entries
1486  * visible to other transactions before we start to build the index. That
1487  * will prevent them from making incompatible HOT updates. The new index
1488  * will be marked not indisready and not indisvalid, so that no one else
1489  * tries to either insert into it or use it for queries.
1490  *
1491  * We must commit our current transaction so that the index becomes
1492  * visible; then start another. Note that all the data structures we just
1493  * built are lost in the commit. The only data we keep past here are the
1494  * relation IDs.
1495  *
1496  * Before committing, get a session-level lock on the table, to ensure
1497  * that neither it nor the index can be dropped before we finish. This
1498  * cannot block, even if someone else is waiting for access, because we
1499  * already have the same lock within our transaction.
1500  *
1501  * Note: we don't currently bother with a session lock on the index,
1502  * because there are no operations that could change its state while we
1503  * hold lock on the parent table. This might need to change later.
1504  */
1506 
1510 
1511  /* Tell concurrent index builds to ignore us, if index qualifies */
1512  if (safe_index)
1514 
1515  /*
1516  * The index is now visible, so we can report the OID. While on it,
1517  * include the report for the beginning of phase 2.
1518  */
1519  {
1520  const int progress_cols[] = {
1523  };
1524  const int64 progress_vals[] = {
1525  indexRelationId,
1527  };
1528 
1529  pgstat_progress_update_multi_param(2, progress_cols, progress_vals);
1530  }
1531 
1532  /*
1533  * Phase 2 of concurrent index build (see comments for validate_index()
1534  * for an overview of how this works)
1535  *
1536  * Now we must wait until no running transaction could have the table open
1537  * with the old list of indexes. Use ShareLock to consider running
1538  * transactions that hold locks that permit writing to the table. Note we
1539  * do not need to worry about xacts that open the table for writing after
1540  * this point; they will see the new index when they open it.
1541  *
1542  * Note: the reason we use actual lock acquisition here, rather than just
1543  * checking the ProcArray and sleeping, is that deadlock is possible if
1544  * one of the transactions in question is blocked trying to acquire an
1545  * exclusive lock on our table. The lock code will detect deadlock and
1546  * error out properly.
1547  */
1548  WaitForLockers(heaplocktag, ShareLock, true);
1549 
1550  /*
1551  * At this moment we are sure that there are no transactions with the
1552  * table open for write that don't have this new index in their list of
1553  * indexes. We have waited out all the existing transactions and any new
1554  * transaction will have the new index in its list, but the index is still
1555  * marked as "not-ready-for-inserts". The index is consulted while
1556  * deciding HOT-safety though. This arrangement ensures that no new HOT
1557  * chains can be created where the new tuple and the old tuple in the
1558  * chain have different index keys.
1559  *
1560  * We now take a new snapshot, and build the index using all tuples that
1561  * are visible in this snapshot. We can be sure that any HOT updates to
1562  * these tuples will be compatible with the index, since any updates made
1563  * by transactions that didn't know about the index are now committed or
1564  * rolled back. Thus, each visible tuple is either the end of its
1565  * HOT-chain or the extension of the chain is HOT-safe for this index.
1566  */
1567 
1568  /* Set ActiveSnapshot since functions in the indexes may need it */
1570 
1571  /* Perform concurrent build of index */
1572  index_concurrently_build(relationId, indexRelationId);
1573 
1574  /* we can do away with our snapshot */
1576 
1577  /*
1578  * Commit this transaction to make the indisready update visible.
1579  */
1582 
1583  /* Tell concurrent index builds to ignore us, if index qualifies */
1584  if (safe_index)
1586 
1587  /*
1588  * Phase 3 of concurrent index build
1589  *
1590  * We once again wait until no transaction can have the table open with
1591  * the index marked as read-only for updates.
1592  */
1595  WaitForLockers(heaplocktag, ShareLock, true);
1596 
1597  /*
1598  * Now take the "reference snapshot" that will be used by validate_index()
1599  * to filter candidate tuples. Beware! There might still be snapshots in
1600  * use that treat some transaction as in-progress that our reference
1601  * snapshot treats as committed. If such a recently-committed transaction
1602  * deleted tuples in the table, we will not include them in the index; yet
1603  * those transactions which see the deleting one as still-in-progress will
1604  * expect such tuples to be there once we mark the index as valid.
1605  *
1606  * We solve this by waiting for all endangered transactions to exit before
1607  * we mark the index as valid.
1608  *
1609  * We also set ActiveSnapshot to this snap, since functions in indexes may
1610  * need a snapshot.
1611  */
1613  PushActiveSnapshot(snapshot);
1614 
1615  /*
1616  * Scan the index and the heap, insert any missing index entries.
1617  */
1618  validate_index(relationId, indexRelationId, snapshot);
1619 
1620  /*
1621  * Drop the reference snapshot. We must do this before waiting out other
1622  * snapshot holders, else we will deadlock against other processes also
1623  * doing CREATE INDEX CONCURRENTLY, which would see our snapshot as one
1624  * they must wait for. But first, save the snapshot's xmin to use as
1625  * limitXmin for GetCurrentVirtualXIDs().
1626  */
1627  limitXmin = snapshot->xmin;
1628 
1630  UnregisterSnapshot(snapshot);
1631 
1632  /*
1633  * The snapshot subsystem could still contain registered snapshots that
1634  * are holding back our process's advertised xmin; in particular, if
1635  * default_transaction_isolation = serializable, there is a transaction
1636  * snapshot that is still active. The CatalogSnapshot is likewise a
1637  * hazard. To ensure no deadlocks, we must commit and start yet another
1638  * transaction, and do our wait before any snapshot has been taken in it.
1639  */
1642 
1643  /* Tell concurrent index builds to ignore us, if index qualifies */
1644  if (safe_index)
1646 
1647  /* We should now definitely not be advertising any xmin. */
1649 
1650  /*
1651  * The index is now valid in the sense that it contains all currently
1652  * interesting tuples. But since it might not contain tuples deleted just
1653  * before the reference snap was taken, we have to wait out any
1654  * transactions that might have older snapshots.
1655  */
1658  WaitForOlderSnapshots(limitXmin, true);
1659 
1660  /*
1661  * Index can now be marked valid -- update its pg_index entry
1662  */
1664 
1665  /*
1666  * The pg_index update will cause backends (including this one) to update
1667  * relcache entries for the index itself, but we should also send a
1668  * relcache inval on the parent table to force replanning of cached plans.
1669  * Otherwise existing sessions might fail to use the new index where it
1670  * would be useful. (Note that our earlier commits did not create reasons
1671  * to replan; so relcache flush on the index itself was sufficient.)
1672  */
1674 
1675  /*
1676  * Last thing to do is release the session-level lock on the parent table.
1677  */
1679 
1681 
1682  return address;
1683 }
AclResult
Definition: acl.h:181
@ ACLCHECK_OK
Definition: acl.h:182
AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:5121
AclResult pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:5109
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3512
bytea *(* amoptions_function)(Datum reloptions, bool validate)
Definition: amapi.h:140
void free_attrmap(AttrMap *map)
Definition: attmap.c:57
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc)
Definition: attmap.c:174
int16 AttrNumber
Definition: attnum.h:21
char * get_tablespace_name(Oid spc_oid)
Definition: tablespace.c:1520
Oid get_tablespace_oid(const char *tablespacename, bool missing_ok)
Definition: tablespace.c:1474
Oid GetDefaultTablespace(char relpersistence, bool partitioned)
Definition: tablespace.c:1191
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
void pgstat_progress_update_param(int index, int64 val)
void pgstat_progress_update_multi_param(int nparam, const int *index, const int64 *val)
void pgstat_progress_end_command(void)
@ PROGRESS_COMMAND_CREATE_INDEX
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:427
uint16 bits16
Definition: c.h:449
#define InvalidSubTransactionId
Definition: c.h:593
uint32 TransactionId
Definition: c.h:587
void CreateComments(Oid oid, Oid classoid, int32 subid, const char *comment)
Definition: comment.c:143
int errmsg_internal(const char *fmt,...)
Definition: elog.c:991
#define DEBUG1
Definition: elog.h:24
#define NOTICE
Definition: elog.h:29
bool allowSystemTableMods
Definition: globals.c:124
Oid MyDatabaseTableSpace
Definition: globals.c:91
int set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel, bool is_reload)
Definition: guc.c:7564
@ GUC_ACTION_SAVE
Definition: guc.h:200
@ PGC_S_SESSION
Definition: guc.h:123
@ PGC_USERSET
Definition: guc.h:76
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:680
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
void validate_index(Oid heapId, Oid indexId, Snapshot snapshot)
Definition: index.c:3291
bool CompareIndexInfo(IndexInfo *info1, IndexInfo *info2, Oid *collations1, Oid *collations2, Oid *opfamilies1, Oid *opfamilies2, AttrMap *attmap)
Definition: index.c:2525
void index_set_state_flags(Oid indexId, IndexStateFlagsAction action)
Definition: index.c:3439
Oid index_create(Relation heapRelation, const char *indexRelationName, Oid indexRelationId, Oid parentIndexRelid, Oid parentConstraintId, Oid relFileNode, IndexInfo *indexInfo, List *indexColNames, Oid accessMethodObjectId, Oid tableSpaceId, Oid *collationObjectId, Oid *classObjectId, int16 *coloptions, Datum reloptions, bits16 flags, bits16 constr_flags, bool allow_system_table_mods, bool is_internal, Oid *constraintId)
Definition: index.c:701
void index_check_primary_key(Relation heapRel, IndexInfo *indexInfo, bool is_alter_table, IndexStmt *stmt)
Definition: index.c:205
void index_concurrently_build(Oid heapRelationId, Oid indexRelationId)
Definition: index.c:1444
IndexInfo * BuildIndexInfo(Relation index)
Definition: index.c:2418
#define INDEX_CREATE_IS_PRIMARY
Definition: index.h:61
#define INDEX_CREATE_IF_NOT_EXISTS
Definition: index.h:65
#define INDEX_CREATE_PARTITIONED
Definition: index.h:66
#define INDEX_CREATE_INVALID
Definition: index.h:67
#define INDEX_CREATE_ADD_CONSTRAINT
Definition: index.h:62
#define INDEX_CREATE_SKIP_BUILD
Definition: index.h:63
#define INDEX_CONSTR_CREATE_DEFERRABLE
Definition: index.h:90
@ INDEX_CREATE_SET_VALID
Definition: index.h:27
#define INDEX_CONSTR_CREATE_INIT_DEFERRED
Definition: index.h:91
#define INDEX_CREATE_CONCURRENT
Definition: index.h:64
ObjectAddress DefineIndex(Oid relationId, IndexStmt *stmt, Oid indexRelationId, Oid parentIndexId, Oid parentConstraintId, bool is_alter_table, bool check_rights, bool check_not_in_use, bool skip_build, bool quiet)
Definition: indexcmds.c:521
static void set_indexsafe_procflags(void)
Definition: indexcmds.c:4314
void IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
Definition: indexcmds.c:4150
static char * ChooseIndexName(const char *tabname, Oid namespaceId, List *colnames, List *exclusionOpNames, bool primary, bool isconstraint)
Definition: indexcmds.c:2439
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition: indexcmds.c:416
static void CheckPredicate(Expr *predicate)
Definition: indexcmds.c:1725
static List * ChooseIndexColumnNames(List *indexElems)
Definition: indexcmds.c:2528
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:301
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition: inval.c:1422
int j
Definition: isn.c:74
void list_free(List *list)
Definition: list.c:1505
List * list_concat_copy(const List *list1, const List *list2)
Definition: list.c:577
void LockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
Definition: lmgr.c:398
void WaitForLockers(LOCKTAG heaplocktag, LOCKMODE lockmode, bool progress)
Definition: lmgr.c:986
void UnlockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
Definition: lmgr.c:411
#define SET_LOCKTAG_RELATION(locktag, dboid, reloid)
Definition: lock.h:184
int LOCKMODE
Definition: lockdefs.h:26
#define ShareUpdateExclusiveLock
Definition: lockdefs.h:39
#define ShareLock
Definition: lockdefs.h:40
#define RowExclusiveLock
Definition: lockdefs.h:38
char get_rel_persistence(Oid relid)
Definition: lsyscache.c:2059
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3326
bool get_opclass_opfamily_and_input_type(Oid opclass, Oid *opfamily, Oid *opcintype)
Definition: lsyscache.c:1238
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:164
List * make_ands_implicit(Expr *clause)
Definition: makefuncs.c:720
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:406
#define SECURITY_RESTRICTED_OPERATION
Definition: miscadmin.h:313
Oid GetUserId(void)
Definition: miscinit.c:492
#define copyObject(obj)
Definition: nodes.h:689
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
@ OBJECT_SCHEMA
Definition: parsenodes.h:2170
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2176
#define ACL_CREATE
Definition: parsenodes.h:91
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:834
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition: partdesc.c:72
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
Oid get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
void ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId, Oid childTableId)
bool has_superclass(Oid relationId)
Definition: pg_inherits.c:378
#define lfirst_oid(lc)
Definition: pg_list.h:171
#define PROGRESS_CREATEIDX_PARTITIONS_DONE
Definition: progress.h:87
#define PROGRESS_CREATEIDX_PHASE_WAIT_1
Definition: progress.h:91
#define PROGRESS_CREATEIDX_COMMAND_CREATE_CONCURRENTLY
Definition: progress.h:109
#define PROGRESS_CREATEIDX_ACCESS_METHOD_OID
Definition: progress.h:81
#define PROGRESS_CREATEIDX_PHASE_WAIT_3
Definition: progress.h:97
#define PROGRESS_CREATEIDX_COMMAND_CREATE
Definition: progress.h:108
#define PROGRESS_CREATEIDX_PHASE_WAIT_2
Definition: progress.h:93
#define PROGRESS_CREATEIDX_PHASE
Definition: progress.h:82
#define PROGRESS_CREATEIDX_INDEX_OID
Definition: progress.h:80
#define PROGRESS_CREATEIDX_PARTITIONS_TOTAL
Definition: progress.h:86
#define PROGRESS_CREATEIDX_COMMAND
Definition: progress.h:79
#define RelationGetDescr(relation)
Definition: rel.h:514
#define RelationGetRelationName(relation)
Definition: rel.h:522
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:642
#define RelationGetNamespace(relation)
Definition: rel.h:529
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4675
bytea * index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
Definition: reloptions.c:2059
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:250
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:869
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:827
void PopActiveSnapshot(void)
Definition: snapmgr.c:776
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:682
PGPROC * MyProc
Definition: proc.c:68
#define HTEqualStrategyNumber
Definition: stratnum.h:41
#define BTEqualStrategyNumber
Definition: stratnum.h:31
Definition: attmap.h:35
ItemPointerData t_self
Definition: htup.h:65
amoptions_function amoptions
Definition: amapi.h:266
amgettuple_function amgettuple
Definition: amapi.h:273
bool amcanunique
Definition: amapi.h:226
bool amcanmulticol
Definition: amapi.h:228
bool amcaninclude
Definition: amapi.h:244
List * ii_Predicate
Definition: execnodes.h:166
bool unique
Definition: parsenodes.h:3253
bool reset_default_tblspc
Definition: parsenodes.h:3262
bool deferrable
Definition: parsenodes.h:3257
List * indexParams
Definition: parsenodes.h:3241
Oid indexOid
Definition: parsenodes.h:3248
bool initdeferred
Definition: parsenodes.h:3258
RangeVar * relation
Definition: parsenodes.h:3238
SubTransactionId oldFirstRelfilenodeSubid
Definition: parsenodes.h:3251
List * options
Definition: parsenodes.h:3244
char * tableSpace
Definition: parsenodes.h:3240
SubTransactionId oldCreateSubid
Definition: parsenodes.h:3250
bool isconstraint
Definition: parsenodes.h:3256
List * excludeOpNames
Definition: parsenodes.h:3246
bool nulls_not_distinct
Definition: parsenodes.h:3254
bool concurrent
Definition: parsenodes.h:3260
char * idxname
Definition: parsenodes.h:3237
Node * whereClause
Definition: parsenodes.h:3245
bool if_not_exists
Definition: parsenodes.h:3261
char * accessMethod
Definition: parsenodes.h:3239
char * idxcomment
Definition: parsenodes.h:3247
bool primary
Definition: parsenodes.h:3255
List * indexIncludingParams
Definition: parsenodes.h:3242
Definition: lock.h:168
LockRelId lockRelId
Definition: rel.h:45
Definition: rel.h:38
Oid relId
Definition: rel.h:39
Oid dbId
Definition: rel.h:40
TransactionId xmin
Definition: proc.h:176
bool inh
Definition: primnodes.h:69
LockInfoData rd_lockInfo
Definition: rel.h:112
Form_pg_index rd_index
Definition: rel.h:187
Oid * rd_opfamily
Definition: rel.h:202
Oid * rd_indcollation
Definition: rel.h:212
Form_pg_class rd_rel
Definition: rel.h:109
TransactionId xmin
Definition: snapshot.h:157
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:3989
#define InvalidTransactionId
Definition: transam.h:31
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:288
void StartTransactionCommand(void)
Definition: xact.c:2925
void CommitTransactionCommand(void)
Definition: xact.c:3022

References IndexStmt::accessMethod, ACL_CREATE, aclcheck_error(), ACLCHECK_OK, allowSystemTableMods, IndexAmRoutine::amcaninclude, IndexAmRoutine::amcanmulticol, IndexAmRoutine::amcanorder, IndexAmRoutine::amcanunique, IndexAmRoutine::amgettuple, AMNAME, IndexAmRoutine::amoptions, Assert(), AtEOXact_GUC(), bms_is_member(), BTEqualStrategyNumber, build_attrmap_by_name(), BuildIndexInfo(), CacheInvalidateRelcacheByRelid(), CatalogTupleUpdate(), CheckPredicate(), CheckTableNotInUse(), ChooseIndexColumnNames(), ChooseIndexName(), CommitTransactionCommand(), CompareIndexInfo(), ComputeIndexAttrs(), IndexStmt::concurrent, ConstraintSetParentConstraint(), copyObject, CreateComments(), LockRelId::dbId, DEBUG1, IndexStmt::deferrable, elog, ereport, errcode(), errdetail(), errdetail_relkind_not_supported(), errmsg(), errmsg_internal(), ERROR, IndexStmt::excludeOpNames, IndexElem::expr, FirstLowInvalidHeapAttributeNumber, free_attrmap(), get_namespace_name(), get_opclass_family(), get_opclass_opfamily_and_input_type(), get_opfamily_member(), get_rel_persistence(), get_relation_idx_constraint_oid(), get_tablespace_name(), get_tablespace_oid(), GetDefaultTablespace(), GetIndexAmRoutine(), GETSTRUCT, GetTransactionSnapshot(), GetUserId(), GetUserIdAndSecContext(), GUC_ACTION_SAVE, has_superclass(), heap_copytuple(), heap_freetuple(), HeapTupleIsValid, HTEqualStrategyNumber, i, IndexStmt::idxcomment, IndexStmt::idxname, IndexStmt::if_not_exists, IndexInfo::ii_Expressions, IndexInfo::ii_IndexAttrNumbers, IndexInfo::ii_NumIndexAttrs, IndexInfo::ii_NumIndexKeyAttrs, IndexInfo::ii_Predicate, index_check_primary_key(), index_close(), index_concurrently_build(), INDEX_CONSTR_CREATE_DEFERRABLE, INDEX_CONSTR_CREATE_INIT_DEFERRED, index_create(), INDEX_CREATE_ADD_CONSTRAINT, INDEX_CREATE_CONCURRENT, INDEX_CREATE_IF_NOT_EXISTS, INDEX_CREATE_INVALID, INDEX_CREATE_IS_PRIMARY, INDEX_CREATE_PARTITIONED, INDEX_CREATE_SET_VALID, INDEX_CREATE_SKIP_BUILD, INDEX_MAX_KEYS, index_open(), index_reloptions(), index_set_state_flags(), IndexStmt::indexIncludingParams, IndexStmt::indexOid, IndexStmt::indexParams, INDEXRELID, IndexSetParentIndex(), RangeVar::inh, IndexStmt::initdeferred, InvalidOid, InvalidSubTransactionId, InvalidTransactionId, IsBootstrapProcessingMode, IndexStmt::isconstraint, j, sort-test::key, lfirst, lfirst_oid, list_concat_copy(), list_free(), list_length(), LockRelationIdForSession(), LockInfoData::lockRelId, make_ands_implicit(), makeIndexInfo(), map_variable_attnos(), MyDatabaseTableSpace, MyProc, NameStr, NewGUCNestLevel(), NIL, NoLock, NOTICE, PartitionDescData::nparts, IndexStmt::nulls_not_distinct, OBJECT_SCHEMA, OBJECT_TABLESPACE, ObjectAddressSet, ObjectIdGetDatum, OidIsValid, PartitionDescData::oids, IndexStmt::oldCreateSubid, IndexStmt::oldFirstRelfilenodeSubid, IndexStmt::oldNode, IndexStmt::options, palloc(), PARTITION_STRATEGY_HASH, pfree(), pg_namespace_aclcheck(), pg_tablespace_aclcheck(), PGC_S_SESSION, PGC_USERSET, pgstat_progress_end_command(), pgstat_progress_start_command(), pgstat_progress_update_multi_param(), pgstat_progress_update_param(), PointerGetDatum, PopActiveSnapshot(), IndexStmt::primary, PROGRESS_COMMAND_CREATE_INDEX, PROGRESS_CREATEIDX_ACCESS_METHOD_OID, PROGRESS_CREATEIDX_COMMAND, PROGRESS_CREATEIDX_COMMAND_CREATE, PROGRESS_CREATEIDX_COMMAND_CREATE_CONCURRENTLY, PROGRESS_CREATEIDX_INDEX_OID, PROGRESS_CREATEIDX_PARTITIONS_DONE, PROGRESS_CREATEIDX_PARTITIONS_TOTAL, PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATEIDX_PHASE_WAIT_1, PROGRESS_CREATEIDX_PHASE_WAIT_2, PROGRESS_CREATEIDX_PHASE_WAIT_3, pull_varattnos(), PushActiveSnapshot(), RelationData::rd_indcollation, RelationData::rd_index, RelationData::rd_lockInfo, RelationData::rd_opfamily, RelationData::rd_rel, RegisterSnapshot(), IndexStmt::relation, RELATION_IS_OTHER_TEMP, RelationGetDescr, RelationGetIndexList(), RelationGetNamespace, RelationGetPartitionDesc(), RelationGetPartitionKey(), RelationGetRelationName, ReleaseSysCache(), LockRelId::relId, IndexStmt::reset_default_tblspc, RowExclusiveLock, SearchSysCache1(), SECURITY_RESTRICTED_OPERATION, set_config_option(), set_indexsafe_procflags(), SET_LOCKTAG_RELATION, SetUserIdAndSecContext(), ShareLock, ShareUpdateExclusiveLock, StartTransactionCommand(), HeapTupleData::t_self, table_close(), table_open(), IndexStmt::tableSpace, transformRelOptions(), TupleDescAttr, IndexStmt::unique, UnlockRelationIdForSession(), UnregisterSnapshot(), validate_index(), WaitForLockers(), WaitForOlderSnapshots(), IndexStmt::whereClause, PGPROC::xmin, and SnapshotData::xmin.

Referenced by ATExecAddIndex(), AttachPartitionEnsureIndexes(), DefineRelation(), and ProcessUtilitySlow().

◆ ExecReindex()

void ExecReindex ( ParseState pstate,
ReindexStmt stmt,
bool  isTopLevel 
)

Definition at line 2589 of file indexcmds.c.

2590 {
2591  ReindexParams params = {0};
2592  ListCell *lc;
2593  bool concurrently = false;
2594  bool verbose = false;
2595  char *tablespacename = NULL;
2596 
2597  /* Parse option list */
2598  foreach(lc, stmt->params)
2599  {
2600  DefElem *opt = (DefElem *) lfirst(lc);
2601 
2602  if (strcmp(opt->defname, "verbose") == 0)
2603  verbose = defGetBoolean(opt);
2604  else if (strcmp(opt->defname, "concurrently") == 0)
2605  concurrently = defGetBoolean(opt);
2606  else if (strcmp(opt->defname, "tablespace") == 0)
2607  tablespacename = defGetString(opt);
2608  else
2609  ereport(ERROR,
2610  (errcode(ERRCODE_SYNTAX_ERROR),
2611  errmsg("unrecognized REINDEX option \"%s\"",
2612  opt->defname),
2613  parser_errposition(pstate, opt->location)));
2614  }
2615 
2616  if (concurrently)
2617  PreventInTransactionBlock(isTopLevel,
2618  "REINDEX CONCURRENTLY");
2619 
2620  params.options =
2621  (verbose ? REINDEXOPT_VERBOSE : 0) |
2622  (concurrently ? REINDEXOPT_CONCURRENTLY : 0);
2623 
2624  /*
2625  * Assign the tablespace OID to move indexes to, with InvalidOid to do
2626  * nothing.
2627  */
2628  if (tablespacename != NULL)
2629  {
2630  params.tablespaceOid = get_tablespace_oid(tablespacename, false);
2631 
2632  /* Check permissions except when moving to database's default */
2633  if (OidIsValid(params.tablespaceOid) &&
2635  {
2636  AclResult aclresult;
2637 
2638  aclresult = pg_tablespace_aclcheck(params.tablespaceOid,
2639  GetUserId(), ACL_CREATE);
2640  if (aclresult != ACLCHECK_OK)
2641  aclcheck_error(aclresult, OBJECT_TABLESPACE,
2643  }
2644  }
2645  else
2646  params.tablespaceOid = InvalidOid;
2647 
2648  switch (stmt->kind)
2649  {
2650  case REINDEX_OBJECT_INDEX:
2651  ReindexIndex(stmt->relation, &params, isTopLevel);
2652  break;
2653  case REINDEX_OBJECT_TABLE:
2654  ReindexTable(stmt->relation, &params, isTopLevel);
2655  break;
2656  case REINDEX_OBJECT_SCHEMA:
2657  case REINDEX_OBJECT_SYSTEM:
2659 
2660  /*
2661  * This cannot run inside a user transaction block; if we were
2662  * inside a transaction, then its commit- and
2663  * start-transaction-command calls would not have the intended
2664  * effect!
2665  */
2666  PreventInTransactionBlock(isTopLevel,
2667  (stmt->kind == REINDEX_OBJECT_SCHEMA) ? "REINDEX SCHEMA" :
2668  (stmt->kind == REINDEX_OBJECT_SYSTEM) ? "REINDEX SYSTEM" :
2669  "REINDEX DATABASE");
2670  ReindexMultipleTables(stmt->name, stmt->kind, &params);
2671  break;
2672  default:
2673  elog(ERROR, "unrecognized object type: %d",
2674  (int) stmt->kind);
2675  break;
2676  }
2677 }
bool defGetBoolean(DefElem *def)
Definition: define.c:108
char * defGetString(DefElem *def)
Definition: define.c:49
#define REINDEXOPT_CONCURRENTLY
Definition: index.h:44
#define REINDEXOPT_VERBOSE
Definition: index.h:41
static void ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind, ReindexParams *params)
Definition: indexcmds.c:2865
static void ReindexIndex(RangeVar *indexRelation, ReindexParams *params, bool isTopLevel)
Definition: indexcmds.c:2684
static Oid ReindexTable(RangeVar *relation, ReindexParams *params, bool isTopLevel)
Definition: indexcmds.c:2807
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:110
@ REINDEX_OBJECT_DATABASE
Definition: parsenodes.h:3844
@ REINDEX_OBJECT_INDEX
Definition: parsenodes.h:3840
@ REINDEX_OBJECT_SCHEMA
Definition: parsenodes.h:3842
@ REINDEX_OBJECT_SYSTEM
Definition: parsenodes.h:3843
@ REINDEX_OBJECT_TABLE
Definition: parsenodes.h:3841
static int verbose
char * defname
Definition: parsenodes.h:765
int location
Definition: parsenodes.h:769
Oid tablespaceOid
Definition: index.h:36
bits32 options
Definition: index.h:35
const char * name
Definition: parsenodes.h:3853
ReindexObjectType kind
Definition: parsenodes.h:3850
RangeVar * relation
Definition: parsenodes.h:3852
List * params
Definition: parsenodes.h:3854
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition: xact.c:3462

References ACL_CREATE, aclcheck_error(), ACLCHECK_OK, defGetBoolean(), defGetString(), DefElem::defname, elog, ereport, errcode(), errmsg(), ERROR, get_tablespace_name(), get_tablespace_oid(), GetUserId(), InvalidOid, ReindexStmt::kind, lfirst, DefElem::location, MyDatabaseTableSpace, ReindexStmt::name, OBJECT_TABLESPACE, OidIsValid, ReindexParams::options, ReindexStmt::params, parser_errposition(), pg_tablespace_aclcheck(), PreventInTransactionBlock(), REINDEX_OBJECT_DATABASE, REINDEX_OBJECT_INDEX, REINDEX_OBJECT_SCHEMA, REINDEX_OBJECT_SYSTEM, REINDEX_OBJECT_TABLE, ReindexIndex(), ReindexMultipleTables(), REINDEXOPT_CONCURRENTLY, REINDEXOPT_VERBOSE, ReindexTable(), ReindexStmt::relation, ReindexParams::tablespaceOid, and verbose.

Referenced by standard_ProcessUtility().

◆ GetDefaultOpClass()

Oid GetDefaultOpClass ( Oid  type_id,
Oid  am_id 
)

Definition at line 2208 of file indexcmds.c.

2209 {
2210  Oid result = InvalidOid;
2211  int nexact = 0;
2212  int ncompatible = 0;
2213  int ncompatiblepreferred = 0;
2214  Relation rel;
2215  ScanKeyData skey[1];
2216  SysScanDesc scan;
2217  HeapTuple tup;
2218  TYPCATEGORY tcategory;
2219 
2220  /* If it's a domain, look at the base type instead */
2221  type_id = getBaseType(type_id);
2222 
2223  tcategory = TypeCategory(type_id);
2224 
2225  /*
2226  * We scan through all the opclasses available for the access method,
2227  * looking for one that is marked default and matches the target type
2228  * (either exactly or binary-compatibly, but prefer an exact match).
2229  *
2230  * We could find more than one binary-compatible match. If just one is
2231  * for a preferred type, use that one; otherwise we fail, forcing the user
2232  * to specify which one he wants. (The preferred-type special case is a
2233  * kluge for varchar: it's binary-compatible to both text and bpchar, so
2234  * we need a tiebreaker.) If we find more than one exact match, then
2235  * someone put bogus entries in pg_opclass.
2236  */
2237  rel = table_open(OperatorClassRelationId, AccessShareLock);
2238 
2239  ScanKeyInit(&skey[0],
2240  Anum_pg_opclass_opcmethod,
2241  BTEqualStrategyNumber, F_OIDEQ,
2242  ObjectIdGetDatum(am_id));
2243 
2244  scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
2245  NULL, 1, skey);
2246 
2247  while (HeapTupleIsValid(tup = systable_getnext(scan)))
2248  {
2249  Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
2250 
2251  /* ignore altogether if not a default opclass */
2252  if (!opclass->opcdefault)
2253  continue;
2254  if (opclass->opcintype == type_id)
2255  {
2256  nexact++;
2257  result = opclass->oid;
2258  }
2259  else if (nexact == 0 &&
2260  IsBinaryCoercible(type_id, opclass->opcintype))
2261  {
2262  if (IsPreferredType(tcategory, opclass->opcintype))
2263  {
2264  ncompatiblepreferred++;
2265  result = opclass->oid;
2266  }
2267  else if (ncompatiblepreferred == 0)
2268  {
2269  ncompatible++;
2270  result = opclass->oid;
2271  }
2272  }
2273  }
2274 
2275  systable_endscan(scan);
2276 
2278 
2279  /* raise error if pg_opclass contains inconsistent data */
2280  if (nexact > 1)
2281  ereport(ERROR,
2283  errmsg("there are multiple default operator classes for data type %s",
2284  format_type_be(type_id))));
2285 
2286  if (nexact == 1 ||
2287  ncompatiblepreferred == 1 ||
2288  (ncompatiblepreferred == 0 && ncompatible == 1))
2289  return result;
2290 
2291  return InvalidOid;
2292 }
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:598
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:505
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:386
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2478
TYPCATEGORY TypeCategory(Oid type)
bool IsBinaryCoercible(Oid srctype, Oid targettype)
bool IsPreferredType(TYPCATEGORY category, Oid type)
char TYPCATEGORY
Definition: parse_coerce.h:21
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32

References AccessShareLock, BTEqualStrategyNumber, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errmsg(), ERROR, format_type_be(), getBaseType(), GETSTRUCT, HeapTupleIsValid, InvalidOid, IsBinaryCoercible(), IsPreferredType(), ObjectIdGetDatum, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), and TypeCategory().

Referenced by ComputePartitionAttrs(), findRangeSubOpclass(), get_opclass(), get_opclass_name(), lookup_type_cache(), ResolveOpClass(), and transformIndexConstraint().

◆ IndexSetParentIndex()

void IndexSetParentIndex ( Relation  partitionIdx,
Oid  parentOid 
)

Definition at line 4150 of file indexcmds.c.

4151 {
4152  Relation pg_inherits;
4153  ScanKeyData key[2];
4154  SysScanDesc scan;
4155  Oid partRelid = RelationGetRelid(partitionIdx);
4156  HeapTuple tuple;
4157  bool fix_dependencies;
4158 
4159  /* Make sure this is an index */
4160  Assert(partitionIdx->rd_rel->relkind == RELKIND_INDEX ||
4161  partitionIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
4162 
4163  /*
4164  * Scan pg_inherits for rows linking our index to some parent.
4165  */
4166  pg_inherits = relation_open(InheritsRelationId, RowExclusiveLock);
4167  ScanKeyInit(&key[0],
4168  Anum_pg_inherits_inhrelid,
4169  BTEqualStrategyNumber, F_OIDEQ,
4170  ObjectIdGetDatum(partRelid));
4171  ScanKeyInit(&key[1],
4172  Anum_pg_inherits_inhseqno,
4173  BTEqualStrategyNumber, F_INT4EQ,
4174  Int32GetDatum(1));
4175  scan = systable_beginscan(pg_inherits, InheritsRelidSeqnoIndexId, true,
4176  NULL, 2, key);
4177  tuple = systable_getnext(scan);
4178 
4179  if (!HeapTupleIsValid(tuple))
4180  {
4181  if (parentOid == InvalidOid)
4182  {
4183  /*
4184  * No pg_inherits row, and no parent wanted: nothing to do in this
4185  * case.
4186  */
4187  fix_dependencies = false;
4188  }
4189  else
4190  {
4191  StoreSingleInheritance(partRelid, parentOid, 1);
4192  fix_dependencies = true;
4193  }
4194  }
4195  else
4196  {
4197  Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(tuple);
4198 
4199  if (parentOid == InvalidOid)
4200  {
4201  /*
4202  * There exists a pg_inherits row, which we want to clear; do so.
4203  */
4204  CatalogTupleDelete(pg_inherits, &tuple->t_self);
4205  fix_dependencies = true;
4206  }
4207  else
4208  {
4209  /*
4210  * A pg_inherits row exists. If it's the same we want, then we're
4211  * good; if it differs, that amounts to a corrupt catalog and
4212  * should not happen.
4213  */
4214  if (inhForm->inhparent != parentOid)
4215  {
4216  /* unexpected: we should not get called in this case */
4217  elog(ERROR, "bogus pg_inherit row: inhrelid %u inhparent %u",
4218  inhForm->inhrelid, inhForm->inhparent);
4219  }
4220 
4221  /* already in the right state */
4222  fix_dependencies = false;
4223  }
4224  }
4225 
4226  /* done with pg_inherits */
4227  systable_endscan(scan);
4228  relation_close(pg_inherits, RowExclusiveLock);
4229 
4230  /* set relhassubclass if an index partition has been added to the parent */
4231  if (OidIsValid(parentOid))
4232  SetRelationHasSubclass(parentOid, true);
4233 
4234  /* set relispartition correctly on the partition */
4235  update_relispartition(partRelid, OidIsValid(parentOid));
4236 
4237  if (fix_dependencies)
4238  {
4239  /*
4240  * Insert/delete pg_depend rows. If setting a parent, add PARTITION
4241  * dependencies on the parent index and the table; if removing a
4242  * parent, delete PARTITION dependencies.
4243  */
4244  if (OidIsValid(parentOid))
4245  {
4246  ObjectAddress partIdx;
4247  ObjectAddress parentIdx;
4248  ObjectAddress partitionTbl;
4249 
4250  ObjectAddressSet(partIdx, RelationRelationId, partRelid);
4251  ObjectAddressSet(parentIdx, RelationRelationId, parentOid);
4252  ObjectAddressSet(partitionTbl, RelationRelationId,
4253  partitionIdx->rd_index->indrelid);
4254  recordDependencyOn(&partIdx, &parentIdx,
4256  recordDependencyOn(&partIdx, &partitionTbl,
4258  }
4259  else
4260  {
4261  deleteDependencyRecordsForClass(RelationRelationId, partRelid,
4262  RelationRelationId,
4264  deleteDependencyRecordsForClass(RelationRelationId, partRelid,
4265  RelationRelationId,
4267  }
4268 
4269  /* make our updates visible */
4271  }
4272 }
@ DEPENDENCY_PARTITION_PRI
Definition: dependency.h:36
@ DEPENDENCY_PARTITION_SEC
Definition: dependency.h:37
static void update_relispartition(Oid relationId, bool newval)
Definition: indexcmds.c:4279
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:350
static void fix_dependencies(ArchiveHandle *AH)
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:44
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:293
void StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
Definition: pg_inherits.c:509
FormData_pg_inherits * Form_pg_inherits
Definition: pg_inherits.h:45
#define Int32GetDatum(X)
Definition: postgres.h:523
#define RelationGetRelid(relation)
Definition: rel.h:488
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:48
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition: tablecmds.c:3234
void CommandCounterIncrement(void)
Definition: xact.c:1074

References Assert(), BTEqualStrategyNumber, CatalogTupleDelete(), CommandCounterIncrement(), deleteDependencyRecordsForClass(), DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, elog, ERROR, fix_dependencies(), GETSTRUCT, HeapTupleIsValid, Int32GetDatum, InvalidOid, sort-test::key, ObjectAddressSet, ObjectIdGetDatum, OidIsValid, RelationData::rd_index, RelationData::rd_rel, recordDependencyOn(), relation_close(), relation_open(), RelationGetRelid, RowExclusiveLock, ScanKeyInit(), SetRelationHasSubclass(), StoreSingleInheritance(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, and update_relispartition().

Referenced by ATExecAttachPartitionIdx(), AttachPartitionEnsureIndexes(), DefineIndex(), and DetachPartitionFinalize().

◆ makeObjectName()

char* makeObjectName ( const char *  name1,
const char *  name2,
const char *  label 
)

Definition at line 2317 of file indexcmds.c.

2318 {
2319  char *name;
2320  int overhead = 0; /* chars needed for label and underscores */
2321  int availchars; /* chars available for name(s) */
2322  int name1chars; /* chars allocated to name1 */
2323  int name2chars; /* chars allocated to name2 */
2324  int ndx;
2325 
2326  name1chars = strlen(name1);
2327  if (name2)
2328  {
2329  name2chars = strlen(name2);
2330  overhead++; /* allow for separating underscore */
2331  }
2332  else
2333  name2chars = 0;
2334  if (label)
2335  overhead += strlen(label) + 1;
2336 
2337  availchars = NAMEDATALEN - 1 - overhead;
2338  Assert(availchars > 0); /* else caller chose a bad label */
2339 
2340  /*
2341  * If we must truncate, preferentially truncate the longer name. This
2342  * logic could be expressed without a loop, but it's simple and obvious as
2343  * a loop.
2344  */
2345  while (name1chars + name2chars > availchars)
2346  {
2347  if (name1chars > name2chars)
2348  name1chars--;
2349  else
2350  name2chars--;
2351  }
2352 
2353  name1chars = pg_mbcliplen(name1, name1chars, name1chars);
2354  if (name2)
2355  name2chars = pg_mbcliplen(name2, name2chars, name2chars);
2356 
2357  /* Now construct the string using the chosen lengths */
2358  name = palloc(name1chars + name2chars + overhead + 1);
2359  memcpy(name, name1, name1chars);
2360  ndx = name1chars;
2361  if (name2)
2362  {
2363  name[ndx++] = '_';
2364  memcpy(name + ndx, name2, name2chars);
2365  ndx += name2chars;
2366  }
2367  if (label)
2368  {
2369  name[ndx++] = '_';
2370  strcpy(name + ndx, label);
2371  }
2372  else
2373  name[ndx] = '\0';
2374 
2375  return name;
2376 }

References Assert(), label, name, NAMEDATALEN, palloc(), and pg_mbcliplen().

Referenced by ChooseConstraintName(), ChooseExtendedStatisticName(), and ChooseRelationName().

◆ RangeVarCallbackForReindexIndex()

static void RangeVarCallbackForReindexIndex ( const RangeVar relation,
Oid  relId,
Oid  oldRelId,
void *  arg 
)
static

Definition at line 2737 of file indexcmds.c.

2739 {
2740  char relkind;
2742  LOCKMODE table_lockmode;
2743 
2744  /*
2745  * Lock level here should match table lock in reindex_index() for
2746  * non-concurrent case and table locks used by index_concurrently_*() for
2747  * concurrent case.
2748  */
2749  table_lockmode = (state->params.options & REINDEXOPT_CONCURRENTLY) != 0 ?
2751 
2752  /*
2753  * If we previously locked some other index's heap, and the name we're
2754  * looking up no longer refers to that relation, release the now-useless
2755  * lock.
2756  */
2757  if (relId != oldRelId && OidIsValid(oldRelId))
2758  {
2759  UnlockRelationOid(state->locked_table_oid, table_lockmode);
2760  state->locked_table_oid = InvalidOid;
2761  }
2762 
2763  /* If the relation does not exist, there's nothing more to do. */
2764  if (!OidIsValid(relId))
2765  return;
2766 
2767  /*
2768  * If the relation does exist, check whether it's an index. But note that
2769  * the relation might have been dropped between the time we did the name
2770  * lookup and now. In that case, there's nothing to do.
2771  */
2772  relkind = get_rel_relkind(relId);
2773  if (!relkind)
2774  return;
2775  if (relkind != RELKIND_INDEX &&
2776  relkind != RELKIND_PARTITIONED_INDEX)
2777  ereport(ERROR,
2778  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2779  errmsg("\"%s\" is not an index", relation->relname)));
2780 
2781  /* Check permissions */
2782  if (!pg_class_ownercheck(relId, GetUserId()))
2784 
2785  /* Lock heap before index to avoid deadlock. */
2786  if (relId != oldRelId)
2787  {
2788  Oid table_oid = IndexGetRelation(relId, true);
2789 
2790  /*
2791  * If the OID isn't valid, it means the index was concurrently
2792  * dropped, which is not a problem for us; just return normally.
2793  */
2794  if (OidIsValid(table_oid))
2795  {
2796  LockRelationOid(table_oid, table_lockmode);
2797  state->locked_table_oid = table_oid;
2798  }
2799  }
2800 }
@ ACLCHECK_NOT_OWNER
Definition: acl.h:184
bool pg_class_ownercheck(Oid class_oid, Oid roleid)
Definition: aclchk.c:5171
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:228
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:109
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:1984
@ OBJECT_INDEX
Definition: parsenodes.h:2154
char * relname
Definition: primnodes.h:68
Definition: regguts.h:318

References aclcheck_error(), ACLCHECK_NOT_OWNER, arg, ereport, errcode(), errmsg(), ERROR, get_rel_relkind(), GetUserId(), IndexGetRelation(), InvalidOid, LockRelationOid(), OBJECT_INDEX, OidIsValid, pg_class_ownercheck(), REINDEXOPT_CONCURRENTLY, RangeVar::relname, ShareLock, ShareUpdateExclusiveLock, and UnlockRelationOid().

Referenced by ReindexIndex().

◆ reindex_error_callback()

static void reindex_error_callback ( void *  args)
static

Definition at line 3072 of file indexcmds.c.

3073 {
3074  ReindexErrorInfo *errinfo = (ReindexErrorInfo *) arg;
3075 
3076  Assert(RELKIND_HAS_PARTITIONS(errinfo->relkind));
3077 
3078  if (errinfo->relkind == RELKIND_PARTITIONED_TABLE)
3079  errcontext("while reindexing partitioned table \"%s.%s\"",
3080  errinfo->relnamespace, errinfo->relname);
3081  else if (errinfo->relkind == RELKIND_PARTITIONED_INDEX)
3082  errcontext("while reindexing partitioned index \"%s.%s\"",
3083  errinfo->relnamespace, errinfo->relname);
3084 }
#define errcontext
Definition: elog.h:190
char * relnamespace
Definition: indexcmds.c:125

References arg, Assert(), errcontext, ReindexErrorInfo::relkind, ReindexErrorInfo::relname, and ReindexErrorInfo::relnamespace.

Referenced by ReindexPartitions().

◆ ReindexIndex()

static void ReindexIndex ( RangeVar indexRelation,
ReindexParams params,
bool  isTopLevel 
)
static

Definition at line 2684 of file indexcmds.c.

2685 {
2687  Oid indOid;
2688  char persistence;
2689  char relkind;
2690 
2691  /*
2692  * Find and lock index, and check permissions on table; use callback to
2693  * obtain lock on table first, to avoid deadlock hazard. The lock level
2694  * used here must match the index lock obtained in reindex_index().
2695  *
2696  * If it's a temporary index, we will perform a non-concurrent reindex,
2697  * even if CONCURRENTLY was requested. In that case, reindex_index() will
2698  * upgrade the lock, but that's OK, because other sessions can't hold
2699  * locks on our temporary table.
2700  */
2701  state.params = *params;
2702  state.locked_table_oid = InvalidOid;
2703  indOid = RangeVarGetRelidExtended(indexRelation,
2706  0,
2708  &state);
2709 
2710  /*
2711  * Obtain the current persistence and kind of the existing index. We
2712  * already hold a lock on the index.
2713  */
2714  persistence = get_rel_persistence(indOid);
2715  relkind = get_rel_relkind(indOid);
2716 
2717  if (relkind == RELKIND_PARTITIONED_INDEX)
2718  ReindexPartitions(indOid, params, isTopLevel);
2719  else if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
2720  persistence != RELPERSISTENCE_TEMP)
2722  else
2723  {
2724  ReindexParams newparams = *params;
2725 
2726  newparams.options |= REINDEXOPT_REPORT_PROGRESS;
2727  reindex_index(indOid, false, persistence, &newparams);
2728  }
2729 }
void reindex_index(Oid indexId, bool skip_constraint_checks, char persistence, ReindexParams *params)
Definition: index.c:3545
#define REINDEXOPT_REPORT_PROGRESS
Definition: index.h:42
static bool ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
Definition: indexcmds.c:3311
static void ReindexPartitions(Oid relid, ReindexParams *params, bool isTopLevel)
Definition: indexcmds.c:3093
static void RangeVarCallbackForReindexIndex(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: indexcmds.c:2737
#define AccessExclusiveLock
Definition: lockdefs.h:43
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:238
ReindexParams params
Definition: indexcmds.c:115

References AccessExclusiveLock, get_rel_persistence(), get_rel_relkind(), InvalidOid, ReindexParams::options, ReindexIndexCallbackState::params, RangeVarCallbackForReindexIndex(), RangeVarGetRelidExtended(), reindex_index(), REINDEXOPT_CONCURRENTLY, REINDEXOPT_REPORT_PROGRESS, ReindexPartitions(), ReindexRelationConcurrently(), and ShareUpdateExclusiveLock.

Referenced by ExecReindex().

◆ ReindexMultipleInternal()

static void ReindexMultipleInternal ( List relids,
ReindexParams params 
)
static

Definition at line 3187 of file indexcmds.c.

3188 {
3189  ListCell *l;
3190 
3193 
3194  foreach(l, relids)
3195  {
3196  Oid relid = lfirst_oid(l);
3197  char relkind;
3198  char relpersistence;
3199 
3201 
3202  /* functions in indexes may want a snapshot set */
3204 
3205  /* check if the relation still exists */
3207  {
3210  continue;
3211  }
3212 
3213  /*
3214  * Check permissions except when moving to database's default if a new
3215  * tablespace is chosen. Note that this check also happens in
3216  * ExecReindex(), but we do an extra check here as this runs across
3217  * multiple transactions.
3218  */
3219  if (OidIsValid(params->tablespaceOid) &&
3221  {
3222  AclResult aclresult;
3223 
3224  aclresult = pg_tablespace_aclcheck(params->tablespaceOid,
3225  GetUserId(), ACL_CREATE);
3226  if (aclresult != ACLCHECK_OK)
3227  aclcheck_error(aclresult, OBJECT_TABLESPACE,
3229  }
3230 
3231  relkind = get_rel_relkind(relid);
3232  relpersistence = get_rel_persistence(relid);
3233 
3234  /*
3235  * Partitioned tables and indexes can never be processed directly, and
3236  * a list of their leaves should be built first.
3237  */
3238  Assert(!RELKIND_HAS_PARTITIONS(relkind));
3239 
3240  if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
3241  relpersistence != RELPERSISTENCE_TEMP)
3242  {
3243  ReindexParams newparams = *params;
3244 
3245  newparams.options |= REINDEXOPT_MISSING_OK;
3246  (void) ReindexRelationConcurrently(relid, &newparams);
3247  /* ReindexRelationConcurrently() does the verbose output */
3248  }
3249  else if (relkind == RELKIND_INDEX)
3250  {
3251  ReindexParams newparams = *params;
3252 
3253  newparams.options |=
3255  reindex_index(relid, false, relpersistence, &newparams);
3257  /* reindex_index() does the verbose output */
3258  }
3259  else
3260  {
3261  bool result;
3262  ReindexParams newparams = *params;
3263 
3264  newparams.options |=
3266  result = reindex_relation(relid,
3269  &newparams);
3270 
3271  if (result && (params->options & REINDEXOPT_VERBOSE) != 0)
3272  ereport(INFO,
3273  (errmsg("table \"%s.%s\" was reindexed",
3275  get_rel_name(relid))));
3276 
3278  }
3279 
3281  }
3282 
3284 }
#define INFO
Definition: elog.h:28
bool reindex_relation(Oid relid, int flags, ReindexParams *params)
Definition: index.c:3859
#define REINDEX_REL_PROCESS_TOAST
Definition: index.h:155
#define REINDEXOPT_MISSING_OK
Definition: index.h:43
#define REINDEX_REL_CHECK_CONSTRAINTS
Definition: index.h:157
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:1933
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1909
@ RELOID
Definition: syscache.h:89
#define SearchSysCacheExists1(cacheId, key1)
Definition: syscache.h:188

References ACL_CREATE, aclcheck_error(), ACLCHECK_OK, Assert(), CommitTransactionCommand(), ereport, errmsg(), get_namespace_name(), get_rel_name(), get_rel_namespace(), get_rel_persistence(), get_rel_relkind(), get_tablespace_name(), GetTransactionSnapshot(), GetUserId(), INFO, lfirst_oid, MyDatabaseTableSpace, OBJECT_TABLESPACE, ObjectIdGetDatum, OidIsValid, ReindexParams::options, ReindexIndexCallbackState::params, pg_tablespace_aclcheck(), PopActiveSnapshot(), PushActiveSnapshot(), reindex_index(), REINDEX_REL_CHECK_CONSTRAINTS, REINDEX_REL_PROCESS_TOAST, reindex_relation(), REINDEXOPT_CONCURRENTLY, REINDEXOPT_MISSING_OK, REINDEXOPT_REPORT_PROGRESS, REINDEXOPT_VERBOSE, ReindexRelationConcurrently(), RELOID, SearchSysCacheExists1, StartTransactionCommand(), and ReindexParams::tablespaceOid.

Referenced by ReindexMultipleTables(), and ReindexPartitions().

◆ ReindexMultipleTables()

static void ReindexMultipleTables ( const char *  objectName,
ReindexObjectType  objectKind,
ReindexParams params 
)
static

Definition at line 2865 of file indexcmds.c.

2867 {
2868  Oid objectOid;
2869  Relation relationRelation;
2870  TableScanDesc scan;
2871  ScanKeyData scan_keys[1];
2872  HeapTuple tuple;
2873  MemoryContext private_context;
2874  MemoryContext old;
2875  List *relids = NIL;
2876  int num_keys;
2877  bool concurrent_warning = false;
2878  bool tablespace_warning = false;
2879 
2880  AssertArg(objectName);
2881  Assert(objectKind == REINDEX_OBJECT_SCHEMA ||
2882  objectKind == REINDEX_OBJECT_SYSTEM ||
2883  objectKind == REINDEX_OBJECT_DATABASE);
2884 
2885  if (objectKind == REINDEX_OBJECT_SYSTEM &&
2886  (params->options & REINDEXOPT_CONCURRENTLY) != 0)
2887  ereport(ERROR,
2888  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2889  errmsg("cannot reindex system catalogs concurrently")));
2890 
2891  /*
2892  * Get OID of object to reindex, being the database currently being used
2893  * by session for a database or for system catalogs, or the schema defined
2894  * by caller. At the same time do permission checks that need different
2895  * processing depending on the object type.
2896  */
2897  if (objectKind == REINDEX_OBJECT_SCHEMA)
2898  {
2899  objectOid = get_namespace_oid(objectName, false);
2900 
2901  if (!pg_namespace_ownercheck(objectOid, GetUserId()))
2903  objectName);
2904  }
2905  else
2906  {
2907  objectOid = MyDatabaseId;
2908 
2909  if (strcmp(objectName, get_database_name(objectOid)) != 0)
2910  ereport(ERROR,
2911  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2912  errmsg("can only reindex the currently open database")));
2913  if (!pg_database_ownercheck(objectOid, GetUserId()))
2915  objectName);
2916  }
2917 
2918  /*
2919  * Create a memory context that will survive forced transaction commits we
2920  * do below. Since it is a child of PortalContext, it will go away
2921  * eventually even if we suffer an error; there's no need for special
2922  * abort cleanup logic.
2923  */
2924  private_context = AllocSetContextCreate(PortalContext,
2925  "ReindexMultipleTables",
2927 
2928  /*
2929  * Define the search keys to find the objects to reindex. For a schema, we
2930  * select target relations using relnamespace, something not necessary for
2931  * a database-wide operation.
2932  */
2933  if (objectKind == REINDEX_OBJECT_SCHEMA)
2934  {
2935  num_keys = 1;
2936  ScanKeyInit(&scan_keys[0],
2937  Anum_pg_class_relnamespace,
2938  BTEqualStrategyNumber, F_OIDEQ,
2939  ObjectIdGetDatum(objectOid));
2940  }
2941  else
2942  num_keys = 0;
2943 
2944  /*
2945  * Scan pg_class to build a list of the relations we need to reindex.
2946  *
2947  * We only consider plain relations and materialized views here (toast
2948  * rels will be processed indirectly by reindex_relation).
2949  */
2950  relationRelation = table_open(RelationRelationId, AccessShareLock);
2951  scan = table_beginscan_catalog(relationRelation, num_keys, scan_keys);
2952  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
2953  {
2954  Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
2955  Oid relid = classtuple->oid;
2956 
2957  /*
2958  * Only regular tables and matviews can have indexes, so ignore any
2959  * other kind of relation.
2960  *
2961  * Partitioned tables/indexes are skipped but matching leaf partitions
2962  * are processed.
2963  */
2964  if (classtuple->relkind != RELKIND_RELATION &&
2965  classtuple->relkind != RELKIND_MATVIEW)
2966  continue;
2967 
2968  /* Skip temp tables of other backends; we can't reindex them at all */
2969  if (classtuple->relpersistence == RELPERSISTENCE_TEMP &&
2970  !isTempNamespace(classtuple->relnamespace))
2971  continue;
2972 
2973  /* Check user/system classification, and optionally skip */
2974  if (objectKind == REINDEX_OBJECT_SYSTEM &&
2975  !IsSystemClass(relid, classtuple))
2976  continue;
2977 
2978  /*
2979  * The table can be reindexed if the user is superuser, the table
2980  * owner, or the database/schema owner (but in the latter case, only
2981  * if it's not a shared relation). pg_class_ownercheck includes the
2982  * superuser case, and depending on objectKind we already know that
2983  * the user has permission to run REINDEX on this database or schema
2984  * per the permission checks at the beginning of this routine.
2985  */
2986  if (classtuple->relisshared &&
2987  !pg_class_ownercheck(relid, GetUserId()))
2988  continue;
2989 
2990  /*
2991  * Skip system tables, since index_create() would reject indexing them
2992  * concurrently (and it would likely fail if we tried).
2993  */
2994  if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
2995  IsCatalogRelationOid(relid))
2996  {
2997  if (!concurrent_warning)
2998  ereport(WARNING,
2999  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3000  errmsg("cannot reindex system catalogs concurrently, skipping all")));
3001  concurrent_warning = true;
3002  continue;
3003  }
3004 
3005  /*
3006  * If a new tablespace is set, check if this relation has to be
3007  * skipped.
3008  */
3009  if (OidIsValid(params->tablespaceOid))
3010  {
3011  bool skip_rel = false;
3012 
3013  /*
3014  * Mapped relations cannot be moved to different tablespaces (in
3015  * particular this eliminates all shared catalogs.).
3016  */
3017  if (RELKIND_HAS_STORAGE(classtuple->relkind) &&
3018  !OidIsValid(classtuple->relfilenode))
3019  skip_rel = true;
3020 
3021  /*
3022  * A system relation is always skipped, even with
3023  * allow_system_table_mods enabled.
3024  */
3025  if (IsSystemClass(relid, classtuple))
3026  skip_rel = true;
3027 
3028  if (skip_rel)
3029  {
3030  if (!tablespace_warning)
3031  ereport(WARNING,
3032  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3033  errmsg("cannot move system relations, skipping all")));
3034  tablespace_warning = true;
3035  continue;
3036  }
3037  }
3038 
3039  /* Save the list of relation OIDs in private context */
3040  old = MemoryContextSwitchTo(private_context);
3041 
3042  /*
3043  * We always want to reindex pg_class first if it's selected to be
3044  * reindexed. This ensures that if there is any corruption in
3045  * pg_class' indexes, they will be fixed before we process any other
3046  * tables. This is critical because reindexing itself will try to
3047  * update pg_class.
3048  */
3049  if (relid == RelationRelationId)
3050  relids = lcons_oid(relid, relids);
3051  else
3052  relids = lappend_oid(relids, relid);
3053 
3054  MemoryContextSwitchTo(old);
3055  }
3056  table_endscan(scan);
3057  table_close(relationRelation, AccessShareLock);
3058 
3059  /*
3060  * Process each relation listed in a separate transaction. Note that this
3061  * commits and then starts a new transaction immediately.
3062  */
3063  ReindexMultipleInternal(relids, params);
3064 
3065  MemoryContextDelete(private_context);
3066 }
bool pg_namespace_ownercheck(Oid nsp_oid, Oid roleid)
Definition: aclchk.c:5347
bool pg_database_ownercheck(Oid db_oid, Oid roleid)
Definition: aclchk.c:5589
#define AssertArg(condition)
Definition: c.h:806
bool IsCatalogRelationOid(Oid relid)
Definition: catalog.c:122
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition: catalog.c:87
char * get_database_name(Oid dbid)
Definition: dbcommands.c:2994
#define WARNING
Definition: elog.h:30
Oid MyDatabaseId
Definition: globals.c:89
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1296
static void ReindexMultipleInternal(List *relids, ReindexParams *params)
Definition: indexcmds.c:3187
List * lappend_oid(List *list, Oid datum)
Definition: list.c:372
List * lcons_oid(Oid datum, List *list)
Definition: list.c:510
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218
MemoryContext PortalContext
Definition: mcxt.c:57
#define AllocSetContextCreate
Definition: memutils.h:173
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:207
bool isTempNamespace(Oid namespaceId)
Definition: namespace.c:3203
Oid get_namespace_oid(const char *nspname, bool missing_ok)
Definition: namespace.c:3089
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
@ OBJECT_DATABASE
Definition: parsenodes.h:2143
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
@ ForwardScanDirection
Definition: sdir.h:26
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, struct ScanKeyData *key)
Definition: tableam.c:112
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:993

References AccessShareLock, aclcheck_error(), ACLCHECK_NOT_OWNER, ALLOCSET_SMALL_SIZES, AllocSetContextCreate, Assert(), AssertArg, BTEqualStrategyNumber, ereport, errcode(), errmsg(), ERROR, ForwardScanDirection, get_database_name(), get_namespace_oid(), GETSTRUCT, GetUserId(), heap_getnext(), IsCatalogRelationOid(), IsSystemClass(), isTempNamespace(), lappend_oid(), lcons_oid(), MemoryContextDelete(), MemoryContextSwitchTo(), MyDatabaseId, NIL, OBJECT_DATABASE, OBJECT_SCHEMA, ObjectIdGetDatum, OidIsValid, ReindexParams::options, ReindexIndexCallbackState::params, pg_class_ownercheck(), pg_database_ownercheck(), pg_namespace_ownercheck(), PortalContext, REINDEX_OBJECT_DATABASE, REINDEX_OBJECT_SCHEMA, REINDEX_OBJECT_SYSTEM, ReindexMultipleInternal(), REINDEXOPT_CONCURRENTLY, ScanKeyInit(), table_beginscan_catalog(), table_close(), table_endscan(), table_open(), ReindexParams::tablespaceOid, and WARNING.

Referenced by ExecReindex().

◆ ReindexPartitions()

static void ReindexPartitions ( Oid  relid,
ReindexParams params,
bool  isTopLevel 
)
static

Definition at line 3093 of file indexcmds.c.

3094 {
3095  List *partitions = NIL;
3096  char relkind = get_rel_relkind(relid);
3097  char *relname = get_rel_name(relid);
3098  char *relnamespace = get_namespace_name(get_rel_namespace(relid));
3099  MemoryContext reindex_context;
3100  List *inhoids;
3101  ListCell *lc;
3102  ErrorContextCallback errcallback;
3103  ReindexErrorInfo errinfo;
3104 
3105  Assert(RELKIND_HAS_PARTITIONS(relkind));
3106 
3107  /*
3108  * Check if this runs in a transaction block, with an error callback to
3109  * provide more context under which a problem happens.
3110  */
3111  errinfo.relname = pstrdup(relname);
3112  errinfo.relnamespace = pstrdup(relnamespace);
3113  errinfo.relkind = relkind;
3114  errcallback.callback = reindex_error_callback;
3115  errcallback.arg = (void *) &errinfo;
3116  errcallback.previous = error_context_stack;
3117  error_context_stack = &errcallback;
3118 
3119  PreventInTransactionBlock(isTopLevel,
3120  relkind == RELKIND_PARTITIONED_TABLE ?
3121  "REINDEX TABLE" : "REINDEX INDEX");
3122 
3123  /* Pop the error context stack */
3124  error_context_stack = errcallback.previous;
3125 
3126  /*
3127  * Create special memory context for cross-transaction storage.
3128  *
3129  * Since it is a child of PortalContext, it will go away eventually even
3130  * if we suffer an error so there is no need for special abort cleanup
3131  * logic.
3132  */
3133  reindex_context = AllocSetContextCreate(PortalContext, "Reindex",
3135 
3136  /* ShareLock is enough to prevent schema modifications */
3137  inhoids = find_all_inheritors(relid, ShareLock, NULL);
3138 
3139  /*
3140  * The list of relations to reindex are the physical partitions of the
3141  * tree so discard any partitioned table or index.
3142  */
3143  foreach(lc, inhoids)
3144  {
3145  Oid partoid = lfirst_oid(lc);
3146  char partkind = get_rel_relkind(partoid);
3147  MemoryContext old_context;
3148 
3149  /*
3150  * This discards partitioned tables, partitioned indexes and foreign
3151  * tables.
3152  */
3153  if (!RELKIND_HAS_STORAGE(partkind))
3154  continue;
3155 
3156  Assert(partkind == RELKIND_INDEX ||
3157  partkind == RELKIND_RELATION);
3158 
3159  /* Save partition OID */
3160  old_context = MemoryContextSwitchTo(reindex_context);
3161  partitions = lappend_oid(partitions, partoid);
3162  MemoryContextSwitchTo(old_context);
3163  }
3164 
3165  /*
3166  * Process each partition listed in a separate transaction. Note that
3167  * this commits and then starts a new transaction immediately.
3168  */
3170 
3171  /*
3172  * Clean up working storage --- note we must do this after
3173  * StartTransactionCommand, else we might be trying to delete the active
3174  * context!
3175  */
3176  MemoryContextDelete(reindex_context);
3177 }
ErrorContextCallback * error_context_stack
Definition: elog.c:93
static void reindex_error_callback(void *args)
Definition: indexcmds.c:3072
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:197
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:256
static int partitions
Definition: pgbench.c:236
struct ErrorContextCallback * previous
Definition: elog.h:232
void(* callback)(void *arg)
Definition: elog.h:233

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, ErrorContextCallback::arg, Assert(), ErrorContextCallback::callback, error_context_stack, find_all_inheritors(), get_namespace_name(), get_rel_name(), get_rel_namespace(), get_rel_relkind(), lappend_oid(), lfirst_oid, MemoryContextDelete(), MemoryContextSwitchTo(), NIL, ReindexIndexCallbackState::params, partitions, PortalContext, PreventInTransactionBlock(), ErrorContextCallback::previous, pstrdup(), reindex_error_callback(), ReindexMultipleInternal(), ReindexErrorInfo::relkind, ReindexErrorInfo::relname, relname, ReindexErrorInfo::relnamespace, and ShareLock.

Referenced by ReindexIndex(), and ReindexTable().

◆ ReindexRelationConcurrently()

static bool ReindexRelationConcurrently ( Oid  relationOid,
ReindexParams params 
)
static

Definition at line 3311 of file indexcmds.c.

3312 {
3313  typedef struct ReindexIndexInfo
3314  {
3315  Oid indexId;
3316  Oid tableId;
3317  Oid amId;
3318  bool safe; /* for set_indexsafe_procflags */
3319  } ReindexIndexInfo;
3320  List *heapRelationIds = NIL;
3321  List *indexIds = NIL;
3322  List *newIndexIds = NIL;
3323  List *relationLocks = NIL;
3324  List *lockTags = NIL;
3325  ListCell *lc,
3326  *lc2;
3327  MemoryContext private_context;
3328  MemoryContext oldcontext;
3329  char relkind;
3330  char *relationName = NULL;
3331  char *relationNamespace = NULL;
3332  PGRUsage ru0;
3333  const int progress_index[] = {
3338  };
3339  int64 progress_vals[4];
3340 
3341  /*
3342  * Create a memory context that will survive forced transaction commits we
3343  * do below. Since it is a child of PortalContext, it will go away
3344  * eventually even if we suffer an error; there's no need for special
3345  * abort cleanup logic.
3346  */
3347  private_context = AllocSetContextCreate(PortalContext,
3348  "ReindexConcurrent",
3350 
3351  if ((params->options & REINDEXOPT_VERBOSE) != 0)
3352  {
3353  /* Save data needed by REINDEX VERBOSE in private context */
3354  oldcontext = MemoryContextSwitchTo(private_context);
3355 
3356  relationName = get_rel_name(relationOid);
3357  relationNamespace = get_namespace_name(get_rel_namespace(relationOid));
3358 
3359  pg_rusage_init(&ru0);
3360 
3361  MemoryContextSwitchTo(oldcontext);
3362  }
3363 
3364  relkind = get_rel_relkind(relationOid);
3365 
3366  /*
3367  * Extract the list of indexes that are going to be rebuilt based on the
3368  * relation Oid given by caller.
3369  */
3370  switch (relkind)
3371  {
3372  case RELKIND_RELATION:
3373  case RELKIND_MATVIEW:
3374  case RELKIND_TOASTVALUE:
3375  {
3376  /*
3377  * In the case of a relation, find all its indexes including
3378  * toast indexes.
3379  */
3380  Relation heapRelation;
3381 
3382  /* Save the list of relation OIDs in private context */
3383  oldcontext = MemoryContextSwitchTo(private_context);
3384 
3385  /* Track this relation for session locks */
3386  heapRelationIds = lappend_oid(heapRelationIds, relationOid);
3387 
3388  MemoryContextSwitchTo(oldcontext);
3389 
3390  if (IsCatalogRelationOid(relationOid))
3391  ereport(ERROR,
3392  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3393  errmsg("cannot reindex system catalogs concurrently")));
3394 
3395  /* Open relation to get its indexes */
3396  if ((params->options & REINDEXOPT_MISSING_OK) != 0)
3397  {
3398  heapRelation = try_table_open(relationOid,
3400  /* leave if relation does not exist */
3401  if (!heapRelation)
3402  break;
3403  }
3404  else
3405  heapRelation = table_open(relationOid,
3407 
3408  if (OidIsValid(params->tablespaceOid) &&
3409  IsSystemRelation(heapRelation))
3410  ereport(ERROR,
3411  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3412  errmsg("cannot move system relation \"%s\"",
3413  RelationGetRelationName(heapRelation))));
3414 
3415  /* Add all the valid indexes of relation to list */
3416  foreach(lc, RelationGetIndexList(heapRelation))
3417  {
3418  Oid cellOid = lfirst_oid(lc);
3419  Relation indexRelation = index_open(cellOid,
3421 
3422  if (!indexRelation->rd_index->indisvalid)
3423  ereport(WARNING,
3424  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3425  errmsg("cannot reindex invalid index \"%s.%s\" concurrently, skipping",
3427  get_rel_name(cellOid))));
3428  else if (indexRelation->rd_index->indisexclusion)
3429  ereport(WARNING,
3430  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3431  errmsg("cannot reindex exclusion constraint index \"%s.%s\" concurrently, skipping",
3433  get_rel_name(cellOid))));
3434  else
3435  {
3436  ReindexIndexInfo *idx;
3437 
3438  /* Save the list of relation OIDs in private context */
3439  oldcontext = MemoryContextSwitchTo(private_context);
3440 
3441  idx = palloc(sizeof(ReindexIndexInfo));
3442  idx->indexId = cellOid;
3443  /* other fields set later */
3444 
3445  indexIds = lappend(indexIds, idx);
3446 
3447  MemoryContextSwitchTo(oldcontext);
3448  }
3449 
3450  index_close(indexRelation, NoLock);
3451  }
3452 
3453  /* Also add the toast indexes */
3454  if (OidIsValid(heapRelation->rd_rel->reltoastrelid))
3455  {
3456  Oid toastOid = heapRelation->rd_rel->reltoastrelid;
3457  Relation toastRelation = table_open(toastOid,
3459 
3460  /* Save the list of relation OIDs in private context */
3461  oldcontext = MemoryContextSwitchTo(private_context);
3462 
3463  /* Track this relation for session locks */
3464  heapRelationIds = lappend_oid(heapRelationIds, toastOid);
3465 
3466  MemoryContextSwitchTo(oldcontext);
3467 
3468  foreach(lc2, RelationGetIndexList(toastRelation))
3469  {
3470  Oid cellOid = lfirst_oid(lc2);
3471  Relation indexRelation = index_open(cellOid,
3473 
3474  if (!indexRelation->rd_index->indisvalid)
3475  ereport(WARNING,
3476  (errcode(ERRCODE_INDEX_CORRUPTED),
3477  errmsg("cannot reindex invalid index \"%s.%s\" concurrently, skipping",
3479  get_rel_name(cellOid))));
3480  else
3481  {
3482  ReindexIndexInfo *idx;
3483 
3484  /*
3485  * Save the list of relation OIDs in private
3486  * context
3487  */
3488  oldcontext = MemoryContextSwitchTo(private_context);
3489 
3490  idx = palloc(sizeof(ReindexIndexInfo));
3491  idx->indexId = cellOid;
3492  indexIds = lappend(indexIds, idx);
3493  /* other fields set later */
3494 
3495  MemoryContextSwitchTo(oldcontext);
3496  }
3497 
3498  index_close(indexRelation, NoLock);
3499  }
3500 
3501  table_close(toastRelation, NoLock);
3502  }
3503 
3504  table_close(heapRelation, NoLock);
3505  break;
3506  }
3507  case RELKIND_INDEX:
3508  {
3509  Oid heapId = IndexGetRelation(relationOid,
3510  (params->options & REINDEXOPT_MISSING_OK) != 0);
3511  Relation heapRelation;
3512  ReindexIndexInfo *idx;
3513 
3514  /* if relation is missing, leave */
3515  if (!OidIsValid(heapId))
3516  break;
3517 
3518  if (IsCatalogRelationOid(heapId))
3519  ereport(ERROR,
3520  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3521  errmsg("cannot reindex system catalogs concurrently")));
3522 
3523  /*
3524  * Don't allow reindex for an invalid index on TOAST table, as
3525  * if rebuilt it would not be possible to drop it. Match
3526  * error message in reindex_index().
3527  */
3528  if (IsToastNamespace(get_rel_namespace(relationOid)) &&
3529  !get_index_isvalid(relationOid))
3530  ereport(ERROR,
3531  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3532  errmsg("cannot reindex invalid index on TOAST table")));
3533 
3534  /*
3535  * Check if parent relation can be locked and if it exists,
3536  * this needs to be done at this stage as the list of indexes
3537  * to rebuild is not complete yet, and REINDEXOPT_MISSING_OK
3538  * should not be used once all the session locks are taken.
3539  */
3540  if ((params->options & REINDEXOPT_MISSING_OK) != 0)
3541  {
3542  heapRelation = try_table_open(heapId,
3544  /* leave if relation does not exist */
3545  if (!heapRelation)
3546  break;
3547  }
3548  else
3549  heapRelation = table_open(heapId,
3551 
3552  if (OidIsValid(params->tablespaceOid) &&
3553  IsSystemRelation(heapRelation))
3554  ereport(ERROR,
3555  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3556  errmsg("cannot move system relation \"%s\"",
3557  get_rel_name(relationOid))));
3558 
3559  table_close(heapRelation, NoLock);
3560 
3561  /* Save the list of relation OIDs in private context */
3562  oldcontext = MemoryContextSwitchTo(private_context);
3563 
3564  /* Track the heap relation of this index for session locks */
3565  heapRelationIds = list_make1_oid(heapId);
3566 
3567  /*
3568  * Save the list of relation OIDs in private context. Note
3569  * that invalid indexes are allowed here.
3570  */
3571  idx = palloc(sizeof(ReindexIndexInfo));
3572  idx->indexId = relationOid;
3573  indexIds = lappend(indexIds, idx);
3574  /* other fields set later */
3575 
3576  MemoryContextSwitchTo(oldcontext);
3577  break;
3578  }
3579 
3580  case RELKIND_PARTITIONED_TABLE:
3581  case RELKIND_PARTITIONED_INDEX:
3582  default:
3583  /* Return error if type of relation is not supported */
3584  ereport(ERROR,
3585  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3586  errmsg("cannot reindex this type of relation concurrently")));
3587  break;
3588  }
3589 
3590  /*
3591  * Definitely no indexes, so leave. Any checks based on
3592  * REINDEXOPT_MISSING_OK should be done only while the list of indexes to
3593  * work on is built as the session locks taken before this transaction
3594  * commits will make sure that they cannot be dropped by a concurrent
3595  * session until this operation completes.
3596  */
3597  if (indexIds == NIL)
3598  {
3600  return false;
3601  }
3602 
3603  /* It's not a shared catalog, so refuse to move it to shared tablespace */
3604  if (params->tablespaceOid == GLOBALTABLESPACE_OID)
3605  ereport(ERROR,
3606  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3607  errmsg("cannot move non-shared relation to tablespace \"%s\"",
3608  get_tablespace_name(params->tablespaceOid))));
3609 
3610  Assert(heapRelationIds != NIL);
3611 
3612  /*-----
3613  * Now we have all the indexes we want to process in indexIds.
3614  *
3615  * The phases now are:
3616  *
3617  * 1. create new indexes in the catalog
3618  * 2. build new indexes
3619  * 3. let new indexes catch up with tuples inserted in the meantime
3620  * 4. swap index names
3621  * 5. mark old indexes as dead
3622  * 6. drop old indexes
3623  *
3624  * We process each phase for all indexes before moving to the next phase,
3625  * for efficiency.
3626  */
3627 
3628  /*
3629  * Phase 1 of REINDEX CONCURRENTLY
3630  *
3631  * Create a new index with the same properties as the old one, but it is
3632  * only registered in catalogs and will be built later. Then get session
3633  * locks on all involved tables. See analogous code in DefineIndex() for
3634  * more detailed comments.
3635  */
3636 
3637  foreach(lc, indexIds)
3638  {
3639  char *concurrentName;
3640  ReindexIndexInfo *idx = lfirst(lc);
3641  ReindexIndexInfo *newidx;
3642  Oid newIndexId;
3643  Relation indexRel;
3644  Relation heapRel;
3645  Oid save_userid;
3646  int save_sec_context;
3647  int save_nestlevel;
3648  Relation newIndexRel;
3649  LockRelId *lockrelid;
3650  Oid tablespaceid;
3651 
3652  indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock);
3653  heapRel = table_open(indexRel->rd_index->indrelid,
3655 
3656  /*
3657  * Switch to the table owner's userid, so that any index functions are
3658  * run as that user. Also lock down security-restricted operations
3659  * and arrange to make GUC variable changes local to this command.
3660  */
3661  GetUserIdAndSecContext(&save_userid, &save_sec_context);
3662  SetUserIdAndSecContext(heapRel->rd_rel->relowner,
3663  save_sec_context | SECURITY_RESTRICTED_OPERATION);
3664  save_nestlevel = NewGUCNestLevel();
3665 
3666  /* determine safety of this index for set_indexsafe_procflags */
3667  idx->safe = (indexRel->rd_indexprs == NIL &&
3668  indexRel->rd_indpred == NIL);
3669  idx->tableId = RelationGetRelid(heapRel);
3670  idx->amId = indexRel->rd_rel->relam;
3671 
3672  /* This function shouldn't be called for temporary relations. */
3673  if (indexRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
3674  elog(ERROR, "cannot reindex a temporary table concurrently");
3675 
3677  idx->tableId);
3678 
3680  progress_vals[1] = 0; /* initializing */
3681  progress_vals[2] = idx->indexId;
3682  progress_vals[3] = idx->amId;
3683  pgstat_progress_update_multi_param(4, progress_index, progress_vals);
3684 
3685  /* Choose a temporary relation name for the new index */
3686  concurrentName = ChooseRelationName(get_rel_name(idx->indexId),
3687  NULL,
3688  "ccnew",
3689  get_rel_namespace(indexRel->rd_index->indrelid),
3690  false);
3691 
3692  /* Choose the new tablespace, indexes of toast tables are not moved */
3693  if (OidIsValid(params->tablespaceOid) &&
3694  heapRel->rd_rel->relkind != RELKIND_TOASTVALUE)
3695  tablespaceid = params->tablespaceOid;
3696  else
3697  tablespaceid = indexRel->rd_rel->reltablespace;
3698 
3699  /* Create new index definition based on given index */
3700  newIndexId = index_concurrently_create_copy(heapRel,
3701  idx->indexId,
3702  tablespaceid,
3703  concurrentName);
3704 
3705  /*
3706  * Now open the relation of the new index, a session-level lock is
3707  * also needed on it.
3708  */
3709  newIndexRel = index_open(newIndexId, ShareUpdateExclusiveLock);
3710 
3711  /*
3712  * Save the list of OIDs and locks in private context
3713  */
3714  oldcontext = MemoryContextSwitchTo(private_context);
3715 
3716  newidx = palloc(sizeof(ReindexIndexInfo));
3717  newidx->indexId = newIndexId;
3718  newidx->safe = idx->safe;
3719  newidx->tableId = idx->tableId;
3720  newidx->amId = idx->amId;
3721 
3722  newIndexIds = lappend(newIndexIds, newidx);
3723 
3724  /*
3725  * Save lockrelid to protect each relation from drop then close
3726  * relations. The lockrelid on parent relation is not taken here to
3727  * avoid multiple locks taken on the same relation, instead we rely on
3728  * parentRelationIds built earlier.
3729  */
3730  lockrelid = palloc(sizeof(*lockrelid));
3731  *lockrelid = indexRel->rd_lockInfo.lockRelId;
3732  relationLocks = lappend(relationLocks, lockrelid);
3733  lockrelid = palloc(sizeof(*lockrelid));
3734  *lockrelid = newIndexRel->rd_lockInfo.lockRelId;
3735  relationLocks = lappend(relationLocks, lockrelid);
3736 
3737  MemoryContextSwitchTo(oldcontext);
3738 
3739  index_close(indexRel, NoLock);
3740  index_close(newIndexRel, NoLock);
3741 
3742  /* Roll back any GUC changes executed by index functions */
3743  AtEOXact_GUC(false, save_nestlevel);
3744 
3745  /* Restore userid and security context */
3746  SetUserIdAndSecContext(save_userid, save_sec_context);
3747 
3748  table_close(heapRel, NoLock);
3749  }
3750 
3751  /*
3752  * Save the heap lock for following visibility checks with other backends
3753  * might conflict with this session.
3754  */
3755  foreach(lc, heapRelationIds)
3756  {
3758  LockRelId *lockrelid;
3759  LOCKTAG *heaplocktag;
3760 
3761  /* Save the list of locks in private context */
3762  oldcontext = MemoryContextSwitchTo(private_context);
3763 
3764  /* Add lockrelid of heap relation to the list of locked relations */
3765  lockrelid = palloc(sizeof(*lockrelid));
3766  *lockrelid = heapRelation->rd_lockInfo.lockRelId;
3767  relationLocks = lappend(relationLocks, lockrelid);
3768 
3769  heaplocktag = (LOCKTAG *) palloc(sizeof(LOCKTAG));
3770 
3771  /* Save the LOCKTAG for this parent relation for the wait phase */
3772  SET_LOCKTAG_RELATION(*heaplocktag, lockrelid->dbId, lockrelid->relId);
3773  lockTags = lappend(lockTags, heaplocktag);
3774 
3775  MemoryContextSwitchTo(oldcontext);
3776 
3777  /* Close heap relation */
3778  table_close(heapRelation, NoLock);
3779  }
3780 
3781  /* Get a session-level lock on each table. */
3782  foreach(lc, relationLocks)
3783  {
3784  LockRelId *lockrelid = (LockRelId *) lfirst(lc);
3785 
3787  }
3788 
3792 
3793  /*
3794  * Because we don't take a snapshot in this transaction, there's no need
3795  * to set the PROC_IN_SAFE_IC flag here.
3796  */
3797 
3798  /*
3799  * Phase 2 of REINDEX CONCURRENTLY
3800  *
3801  * Build the new indexes in a separate transaction for each index to avoid
3802  * having open transactions for an unnecessary long time. But before
3803  * doing that, wait until no running transactions could have the table of
3804  * the index open with the old list of indexes. See "phase 2" in
3805  * DefineIndex() for more details.
3806  */
3807 
3810  WaitForLockersMultiple(lockTags, ShareLock, true);
3812 
3813  foreach(lc, newIndexIds)
3814  {
3815  ReindexIndexInfo *newidx = lfirst(lc);
3816 
3817  /* Start new transaction for this index's concurrent build */
3819 
3820  /*
3821  * Check for user-requested abort. This is inside a transaction so as
3822  * xact.c does not issue a useless WARNING, and ensures that
3823  * session-level locks are cleaned up on abort.
3824  */
3826 
3827  /* Tell concurrent indexing to ignore us, if index qualifies */
3828  if (newidx->safe)
3830 
3831  /* Set ActiveSnapshot since functions in the indexes may need it */
3833 
3834  /*
3835  * Update progress for the index to build, with the correct parent
3836  * table involved.
3837  */
3840  progress_vals[1] = PROGRESS_CREATEIDX_PHASE_BUILD;
3841  progress_vals[2] = newidx->indexId;
3842  progress_vals[3] = newidx->amId;
3843  pgstat_progress_update_multi_param(4, progress_index, progress_vals);
3844 
3845  /* Perform concurrent build of new index */
3846  index_concurrently_build(newidx->tableId, newidx->indexId);
3847 
3850  }
3851 
3853 
3854  /*
3855  * Because we don't take a snapshot or Xid in this transaction, there's no
3856  * need to set the PROC_IN_SAFE_IC flag here.
3857  */
3858 
3859  /*
3860  * Phase 3 of REINDEX CONCURRENTLY
3861  *
3862  * During this phase the old indexes catch up with any new tuples that
3863  * were created during the previous phase. See "phase 3" in DefineIndex()
3864  * for more details.
3865  */
3866 
3869  WaitForLockersMultiple(lockTags, ShareLock, true);
3871 
3872  foreach(lc, newIndexIds)
3873  {
3874  ReindexIndexInfo *newidx = lfirst(lc);
3875  TransactionId limitXmin;
3876  Snapshot snapshot;
3877 
3879 
3880  /*
3881  * Check for user-requested abort. This is inside a transaction so as
3882  * xact.c does not issue a useless WARNING, and ensures that
3883  * session-level locks are cleaned up on abort.
3884  */
3886 
3887  /* Tell concurrent indexing to ignore us, if index qualifies */
3888  if (newidx->safe)
3890 
3891  /*
3892  * Take the "reference snapshot" that will be used by validate_index()
3893  * to filter candidate tuples.
3894  */
3896  PushActiveSnapshot(snapshot);
3897 
3898  /*
3899  * Update progress for the index to build, with the correct parent
3900  * table involved.
3901  */
3903  newidx->tableId);
3905  progress_vals[1] = PROGRESS_CREATEIDX_PHASE_VALIDATE_IDXSCAN;
3906  progress_vals[2] = newidx->indexId;
3907  progress_vals[3] = newidx->amId;
3908  pgstat_progress_update_multi_param(4, progress_index, progress_vals);
3909 
3910  validate_index(newidx->tableId, newidx->indexId, snapshot);
3911 
3912  /*
3913  * We can now do away with our active snapshot, we still need to save
3914  * the xmin limit to wait for older snapshots.
3915  */
3916  limitXmin = snapshot->xmin;
3917 
3919  UnregisterSnapshot(snapshot);
3920 
3921  /*
3922  * To ensure no deadlocks, we must commit and start yet another
3923  * transaction, and do our wait before any snapshot has been taken in
3924  * it.
3925  */
3928 
3929  /*
3930  * The index is now valid in the sense that it contains all currently
3931  * interesting tuples. But since it might not contain tuples deleted
3932  * just before the reference snap was taken, we have to wait out any
3933  * transactions that might have older snapshots.
3934  *
3935  * Because we don't take a snapshot or Xid in this transaction,
3936  * there's no need to set the PROC_IN_SAFE_IC flag here.
3937  */
3940  WaitForOlderSnapshots(limitXmin, true);
3941 
3943  }
3944 
3945  /*
3946  * Phase 4 of REINDEX CONCURRENTLY
3947  *
3948  * Now that the new indexes have been validated, swap each new index with
3949  * its corresponding old index.
3950  *
3951  * We mark the new indexes as valid and the old indexes as not valid at
3952  * the same time to make sure we only get constraint violations from the
3953  * indexes with the correct names.
3954  */
3955 
3957 
3958  /*
3959  * Because this transaction only does catalog manipulations and doesn't do
3960  * any index operations, we can set the PROC_IN_SAFE_IC flag here
3961  * unconditionally.
3962  */
3964 
3965  forboth(lc, indexIds, lc2, newIndexIds)
3966  {
3967  ReindexIndexInfo *oldidx = lfirst(lc);
3968  ReindexIndexInfo *newidx = lfirst(lc2);
3969  char *oldName;
3970 
3971  /*
3972  * Check for user-requested abort. This is inside a transaction so as
3973  * xact.c does not issue a useless WARNING, and ensures that
3974  * session-level locks are cleaned up on abort.
3975  */
3977 
3978  /* Choose a relation name for old index */
3979  oldName = ChooseRelationName(get_rel_name(oldidx->indexId),
3980  NULL,
3981  "ccold",
3982  get_rel_namespace(oldidx->tableId),
3983  false);
3984 
3985  /*
3986  * Swap old index with the new one. This also marks the new one as
3987  * valid and the old one as not valid.
3988  */
3989  index_concurrently_swap(newidx->indexId, oldidx->indexId, oldName);
3990 
3991  /*
3992  * Invalidate the relcache for the table, so that after this commit
3993  * all sessions will refresh any cached plans that might reference the
3994  * index.
3995  */
3996  CacheInvalidateRelcacheByRelid(oldidx->tableId);
3997 
3998  /*
3999  * CCI here so that subsequent iterations see the oldName in the
4000  * catalog and can choose a nonconflicting name for their oldName.
4001  * Otherwise, this could lead to conflicts if a table has two indexes
4002  * whose names are equal for the first NAMEDATALEN-minus-a-few
4003  * characters.
4004  */
4006  }
4007 
4008  /* Commit this transaction and make index swaps visible */
4011 
4012  /*
4013  * While we could set PROC_IN_SAFE_IC if all indexes qualified, there's no
4014  * real need for that, because we only acquire an Xid after the wait is
4015  * done, and that lasts for a very short period.
4016  */
4017 
4018  /*
4019  * Phase 5 of REINDEX CONCURRENTLY
4020  *
4021  * Mark the old indexes as dead. First we must wait until no running
4022  * transaction could be using the index for a query. See also
4023  * index_drop() for more details.
4024  */
4025 
4029 
4030  foreach(lc, indexIds)
4031  {
4032  ReindexIndexInfo *oldidx = lfirst(lc);
4033 
4034  /*
4035  * Check for user-requested abort. This is inside a transaction so as
4036  * xact.c does not issue a useless WARNING, and ensures that
4037  * session-level locks are cleaned up on abort.
4038  */
4040 
4041  index_concurrently_set_dead(oldidx->tableId, oldidx->indexId);
4042  }
4043 
4044  /* Commit this transaction to make the updates visible. */
4047 
4048  /*
4049  * While we could set PROC_IN_SAFE_IC if all indexes qualified, there's no
4050  * real need for that, because we only acquire an Xid after the wait is
4051  * done, and that lasts for a very short period.
4052  */
4053 
4054  /*
4055  * Phase 6 of REINDEX CONCURRENTLY
4056  *
4057  * Drop the old indexes.
4058  */
4059 
4063 
4065 
4066  {
4068 
4069  foreach(lc, indexIds)
4070  {
4071  ReindexIndexInfo *idx = lfirst(lc);
4072  ObjectAddress object;
4073 
4074  object.classId = RelationRelationId;
4075  object.objectId = idx->indexId;
4076  object.objectSubId = 0;
4077 
4078  add_exact_object_address(&object, objects);
4079  }
4080 
4081  /*
4082  * Use PERFORM_DELETION_CONCURRENT_LOCK so that index_drop() uses the
4083  * right lock level.
4084  */
4087  }
4088 
4091 
4092  /*
4093  * Finally, release the session-level lock on the table.
4094  */
4095  foreach(lc, relationLocks)
4096  {
4097  LockRelId *lockrelid = (LockRelId *) lfirst(lc);
4098 
4100  }
4101 
4102  /* Start a new transaction to finish process properly */
4104 
4105  /* Log what we did */
4106  if ((params->options & REINDEXOPT_VERBOSE) != 0)
4107  {
4108  if (relkind == RELKIND_INDEX)
4109  ereport(INFO,
4110  (errmsg("index \"%s.%s\" was reindexed",
4111  relationNamespace, relationName),
4112  errdetail("%s.",
4113  pg_rusage_show(&ru0))));
4114  else
4115  {
4116  foreach(lc, newIndexIds)
4117  {
4118  ReindexIndexInfo *idx = lfirst(lc);
4119  Oid indOid = idx->indexId;
4120 
4121  ereport(INFO,
4122  (errmsg("index \"%s.%s\" was reindexed",
4124  get_rel_name(indOid))));
4125  /* Don't show rusage here, since it's not per index. */
4126  }
4127 
4128  ereport(INFO,
4129  (errmsg("table \"%s.%s\" was reindexed",
4130  relationNamespace, relationName),
4131  errdetail("%s.",
4132  pg_rusage_show(&ru0))));
4133  }
4134  }
4135 
4136  MemoryContextDelete(private_context);
4137 
4139 
4140  return true;
4141 }
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
bool IsToastNamespace(Oid namespaceId)
Definition: catalog.c:202
bool IsSystemRelation(Relation relation)
Definition: catalog.c:75
void performMultipleDeletions(const ObjectAddresses *objects, DropBehavior behavior, int flags)
Definition: dependency.c:376
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2447
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2502
#define PERFORM_DELETION_CONCURRENT_LOCK
Definition: dependency.h:140
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:135
void index_concurrently_set_dead(Oid heapId, Oid indexId)
Definition: index.c:1837
void index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
Definition: index.c:1510
Oid index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, Oid tablespaceOid, const char *newName)
Definition: index.c:1274
void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode, bool progress)
Definition: lmgr.c:908
bool get_index_isvalid(Oid index_oid)
Definition: lsyscache.c:3542
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
@ DROP_RESTRICT
Definition: parsenodes.h:2207
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:446
#define list_make1_oid(x1)
Definition: pg_list.h:236
const char * pg_rusage_show(const PGRUsage *ru0)
Definition: pg_rusage.c:40
void pg_rusage_init(PGRUsage *ru0)
Definition: pg_rusage.c:27
#define PROGRESS_CREATEIDX_PHASE_WAIT_4
Definition: progress.h:98
#define PROGRESS_CREATEIDX_PHASE_BUILD
Definition: progress.h:92
#define PROGRESS_CREATEIDX_COMMAND_REINDEX_CONCURRENTLY
Definition: progress.h:111
#define PROGRESS_CREATEIDX_PHASE_VALIDATE_IDXSCAN
Definition: progress.h:94
#define PROGRESS_CREATEIDX_PHASE_WAIT_5
Definition: progress.h:99
List * rd_indpred
Definition: rel.h:208
List * rd_indexprs
Definition: rel.h:207
Relation try_table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:69

References AccessExclusiveLock, add_exact_object_address(), ALLOCSET_SMALL_SIZES, AllocSetContextCreate, Assert(), AtEOXact_GUC(), CacheInvalidateRelcacheByRelid(), CHECK_FOR_INTERRUPTS, ChooseRelationName(), ObjectAddress::classId, CommandCounterIncrement(), CommitTransactionCommand(), LockRelId::dbId, DROP_RESTRICT, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, forboth, get_index_isvalid(), get_namespace_name(), get_rel_name(), get_rel_namespace(), get_rel_relkind(), get_tablespace_name(), GetTransactionSnapshot(), GetUserIdAndSecContext(), idx(), index_close(), index_concurrently_build(), index_concurrently_create_copy(), index_concurrently_set_dead(), index_concurrently_swap(), index_open(), IndexGetRelation(), INFO, IsCatalogRelationOid(), IsSystemRelation(), IsToastNamespace(), lappend(), lappend_oid(), lfirst, lfirst_oid, list_make1_oid, LockRelationIdForSession(), LockInfoData::lockRelId, MemoryContextDelete(), MemoryContextSwitchTo(), new_object_addresses(), NewGUCNestLevel(), NIL, NoLock, OidIsValid, ReindexParams::options, palloc(), PERFORM_DELETION_CONCURRENT_LOCK, PERFORM_DELETION_INTERNAL, performMultipleDeletions(), pg_rusage_init(), pg_rusage_show(), pgstat_progress_end_command(), pgstat_progress_start_command(), pgstat_progress_update_multi_param(), pgstat_progress_update_param(), PopActiveSnapshot(), PortalContext, PROGRESS_COMMAND_CREATE_INDEX, PROGRESS_CREATEIDX_ACCESS_METHOD_OID, PROGRESS_CREATEIDX_COMMAND, PROGRESS_CREATEIDX_COMMAND_REINDEX_CONCURRENTLY, PROGRESS_CREATEIDX_INDEX_OID, PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATEIDX_PHASE_BUILD, PROGRESS_CREATEIDX_PHASE_VALIDATE_IDXSCAN, PROGRESS_CREATEIDX_PHASE_WAIT_1, PROGRESS_CREATEIDX_PHASE_WAIT_2, PROGRESS_CREATEIDX_PHASE_WAIT_3, PROGRESS_CREATEIDX_PHASE_WAIT_4, PROGRESS_CREATEIDX_PHASE_WAIT_5, PushActiveSnapshot(), RelationData::rd_index, RelationData::rd_indexprs, RelationData::rd_indpred, RelationData::rd_lockInfo, RelationData::rd_rel, RegisterSnapshot(), REINDEXOPT_MISSING_OK, REINDEXOPT_VERBOSE, RelationGetIndexList(), RelationGetRelationName, RelationGetRelid, LockRelId::relId, SECURITY_RESTRICTED_OPERATION, set_indexsafe_procflags(), SET_LOCKTAG_RELATION, SetUserIdAndSecContext(), ShareLock, ShareUpdateExclusiveLock, StartTransactionCommand(), table_close(), table_open(), ReindexParams::tablespaceOid, try_table_open(), UnlockRelationIdForSession(), UnregisterSnapshot(), validate_index(), WaitForLockersMultiple(), WaitForOlderSnapshots(), WARNING, and SnapshotData::xmin.

Referenced by ReindexIndex(), ReindexMultipleInternal(), and ReindexTable().

◆ ReindexTable()

static Oid ReindexTable ( RangeVar relation,
ReindexParams params,
bool  isTopLevel 
)
static

Definition at line 2807 of file indexcmds.c.

2808 {
2809  Oid heapOid;
2810  bool result;
2811 
2812  /*
2813  * The lock level used here should match reindex_relation().
2814  *
2815  * If it's a temporary table, we will perform a non-concurrent reindex,
2816  * even if CONCURRENTLY was requested. In that case, reindex_relation()
2817  * will upgrade the lock, but that's OK, because other sessions can't hold
2818  * locks on our temporary table.
2819  */
2820  heapOid = RangeVarGetRelidExtended(relation,
2821  (params->options & REINDEXOPT_CONCURRENTLY) != 0 ?
2823  0,
2825 
2826  if (get_rel_relkind(heapOid) == RELKIND_PARTITIONED_TABLE)
2827  ReindexPartitions(heapOid, params, isTopLevel);
2828  else if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
2829  get_rel_persistence(heapOid) != RELPERSISTENCE_TEMP)
2830  {
2831  result = ReindexRelationConcurrently(heapOid, params);
2832 
2833  if (!result)
2834  ereport(NOTICE,
2835  (errmsg("table \"%s\" has no indexes that can be reindexed concurrently",
2836  relation->relname)));
2837  }
2838  else
2839  {
2840  ReindexParams newparams = *params;
2841 
2842  newparams.options |= REINDEXOPT_REPORT_PROGRESS;
2843  result = reindex_relation(heapOid,
2846  &newparams);
2847  if (!result)
2848  ereport(NOTICE,
2849  (errmsg("table \"%s\" has no indexes to reindex",
2850  relation->relname)));
2851  }
2852 
2853  return heapOid;
2854 }
void RangeVarCallbackOwnsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:16923

References ereport, errmsg(), get_rel_persistence(), get_rel_relkind(), NOTICE, ReindexParams::options, ReindexIndexCallbackState::params, RangeVarCallbackOwnsTable(), RangeVarGetRelidExtended(), REINDEX_REL_CHECK_CONSTRAINTS, REINDEX_REL_PROCESS_TOAST, reindex_relation(), REINDEXOPT_CONCURRENTLY, REINDEXOPT_REPORT_PROGRESS, ReindexPartitions(), ReindexRelationConcurrently(), RangeVar::relname, ShareLock, and ShareUpdateExclusiveLock.

Referenced by ExecReindex().

◆ ResolveOpClass()

Oid ResolveOpClass ( List opclass,
Oid  attrType,
const char *  accessMethodName,
Oid  accessMethodId 
)

Definition at line 2123 of file indexcmds.c.

2125 {
2126  char *schemaname;
2127  char *opcname;
2128  HeapTuple tuple;
2129  Form_pg_opclass opform;
2130  Oid opClassId,
2131  opInputType;
2132 
2133  if (opclass == NIL)
2134  {
2135  /* no operator class specified, so find the default */
2136  opClassId = GetDefaultOpClass(attrType, accessMethodId);
2137  if (!OidIsValid(opClassId))
2138  ereport(ERROR,
2139  (errcode(ERRCODE_UNDEFINED_OBJECT),
2140  errmsg("data type %s has no default operator class for access method \"%s\"",
2141  format_type_be(attrType), accessMethodName),
2142  errhint("You must specify an operator class for the index or define a default operator class for the data type.")));
2143  return opClassId;
2144  }
2145 
2146  /*
2147  * Specific opclass name given, so look up the opclass.
2148  */
2149 
2150  /* deconstruct the name list */
2151  DeconstructQualifiedName(opclass, &schemaname, &opcname);
2152 
2153  if (schemaname)
2154  {
2155  /* Look in specific schema only */
2156  Oid namespaceId;
2157 
2158  namespaceId = LookupExplicitNamespace(schemaname, false);
2159  tuple = SearchSysCache3(CLAAMNAMENSP,
2160  ObjectIdGetDatum(accessMethodId),
2161  PointerGetDatum(opcname),
2162  ObjectIdGetDatum(namespaceId));
2163  }
2164  else
2165  {
2166  /* Unqualified opclass name, so search the search path */
2167  opClassId = OpclassnameGetOpcid(accessMethodId, opcname);
2168  if (!OidIsValid(opClassId))
2169  ereport(ERROR,
2170  (errcode(ERRCODE_UNDEFINED_OBJECT),
2171  errmsg("operator class \"%s\" does not exist for access method \"%s\"",
2172  opcname, accessMethodName)));
2173  tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opClassId));
2174  }
2175 
2176  if (!HeapTupleIsValid(tuple))
2177  ereport(ERROR,
2178  (errcode(ERRCODE_UNDEFINED_OBJECT),
2179  errmsg("operator class \"%s\" does not exist for access method \"%s\"",
2180  NameListToString(opclass), accessMethodName)));
2181 
2182  /*
2183  * Verify that the index operator class accepts this datatype. Note we
2184  * will accept binary compatibility.
2185  */
2186  opform = (Form_pg_opclass) GETSTRUCT(tuple);
2187  opClassId = opform->oid;
2188  opInputType = opform->opcintype;
2189 
2190  if (!IsBinaryCoercible(attrType, opInputType))
2191  ereport(ERROR,
2192  (errcode(ERRCODE_DATATYPE_MISMATCH),
2193  errmsg("operator class \"%s\" does not accept data type %s",
2194  NameListToString(opclass), format_type_be(attrType))));
2195 
2196  ReleaseSysCache(tuple);
2197 
2198  return opClassId;
2199 }
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:2208
Oid OpclassnameGetOpcid(Oid amid, const char *opcname)
Definition: namespace.c:1846
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition: namespace.c:2939
void DeconstructQualifiedName(List *names, char **nspname_p, char **objname_p)
Definition: namespace.c:2855
char * NameListToString(List *names)
Definition: namespace.c:3148
HeapTuple SearchSysCache3(int cacheId, Datum key1, Datum key2, Datum key3)
Definition: syscache.c:1195
@ CLAOID
Definition: syscache.h:48
@ CLAAMNAMENSP
Definition: syscache.h:47

References CLAAMNAMENSP, CLAOID, DeconstructQualifiedName(), ereport, errcode(), errhint(), errmsg(), ERROR, format_type_be(), GetDefaultOpClass(), GETSTRUCT, HeapTupleIsValid, IsBinaryCoercible(), LookupExplicitNamespace(), NameListToString(), NIL, ObjectIdGetDatum, OidIsValid, OpclassnameGetOpcid(), PointerGetDatum, ReleaseSysCache(), SearchSysCache1(), and SearchSysCache3().

Referenced by ComputeIndexAttrs(), and ComputePartitionAttrs().

◆ set_indexsafe_procflags()

static void set_indexsafe_procflags ( void  )
inlinestatic

Definition at line 4314 of file indexcmds.c.

4315 {
4316  /*
4317  * This should only be called before installing xid or xmin in MyProc;
4318  * otherwise, concurrent processes could see an Xmin that moves backwards.
4319  */
4322 
4323  LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
4326  LWLockRelease(ProcArrayLock);
4327 }
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1196
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1800
@ LW_EXCLUSIVE
Definition: lwlock.h:104
#define PROC_IN_SAFE_IC
Definition: proc.h:56
PROC_HDR * ProcGlobal
Definition: proc.c:80
uint8 statusFlags
Definition: proc.h:227
int pgxactoff
Definition: proc.h:186
TransactionId xid
Definition: proc.h:171
uint8 * statusFlags
Definition: proc.h:371

References Assert(), InvalidTransactionId, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MyProc, PGPROC::pgxactoff, PROC_IN_SAFE_IC, ProcGlobal, PGPROC::statusFlags, PROC_HDR::statusFlags, PGPROC::xid, and PGPROC::xmin.

Referenced by DefineIndex(), and ReindexRelationConcurrently().

◆ update_relispartition()

static void update_relispartition ( Oid  relationId,
bool  newval 
)
static

Definition at line 4279 of file indexcmds.c.

4280 {
4281  HeapTuple tup;
4282  Relation classRel;
4283 
4284  classRel = table_open(RelationRelationId, RowExclusiveLock);
4285  tup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
4286  if (!HeapTupleIsValid(tup))
4287  elog(ERROR, "cache lookup failed for relation %u", relationId);
4288  Assert(((Form_pg_class) GETSTRUCT(tup))->relispartition != newval);
4289  ((Form_pg_class) GETSTRUCT(tup))->relispartition = newval;
4290  CatalogTupleUpdate(classRel, &tup->t_self, tup);
4291  heap_freetuple(tup);
4292  table_close(classRel, RowExclusiveLock);
4293 }
#define newval
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:179

References Assert(), CatalogTupleUpdate(), elog, ERROR, GETSTRUCT, heap_freetuple(), HeapTupleIsValid, newval, ObjectIdGetDatum, RELOID, RowExclusiveLock, SearchSysCacheCopy1, HeapTupleData::t_self, table_close(), and table_open().

Referenced by IndexSetParentIndex().

◆ WaitForOlderSnapshots()

void WaitForOlderSnapshots ( TransactionId  limitXmin,
bool  progress 
)

Definition at line 416 of file indexcmds.c.

417 {
418  int n_old_snapshots;
419  int i;
420  VirtualTransactionId *old_snapshots;
421 
422  old_snapshots = GetCurrentVirtualXIDs(limitXmin, true, false,
424  | PROC_IN_SAFE_IC,
425  &n_old_snapshots);
426  if (progress)
428 
429  for (i = 0; i < n_old_snapshots; i++)
430  {
431  if (!VirtualTransactionIdIsValid(old_snapshots[i]))
432  continue; /* found uninteresting in previous cycle */
433 
434  if (i > 0)
435  {
436  /* see if anything's changed ... */
437  VirtualTransactionId *newer_snapshots;
438  int n_newer_snapshots;
439  int j;
440  int k;
441 
442  newer_snapshots = GetCurrentVirtualXIDs(limitXmin,
443  true, false,
445  | PROC_IN_SAFE_IC,
446  &n_newer_snapshots);
447  for (j = i; j < n_old_snapshots; j++)
448  {
449  if (!VirtualTransactionIdIsValid(old_snapshots[j]))
450  continue; /* found uninteresting in previous cycle */
451  for (k = 0; k < n_newer_snapshots; k++)
452  {
453  if (VirtualTransactionIdEquals(old_snapshots[j],
454  newer_snapshots[k]))
455  break;
456  }
457  if (k >= n_newer_snapshots) /* not there anymore */
458  SetInvalidVirtualTransactionId(old_snapshots[j]);
459  }
460  pfree(newer_snapshots);
461  }
462 
463  if (VirtualTransactionIdIsValid(old_snapshots[i]))
464  {
465  /* If requested, publish who we're going to wait for. */
466  if (progress)
467  {
468  PGPROC *holder = BackendIdGetProc(old_snapshots[i].backendId);
469 
470  if (holder)
472  holder->pid);
473  }
474  VirtualXactLock(old_snapshots[i], true);
475  }
476 
477  if (progress)
479  }
480 }
bool VirtualXactLock(VirtualTransactionId vxid, bool wait)
Definition: lock.c:4599
#define VirtualTransactionIdIsValid(vxid)
Definition: lock.h:72
#define VirtualTransactionIdEquals(vxid1, vxid2)
Definition: lock.h:76
#define SetInvalidVirtualTransactionId(vxid)
Definition: lock.h:79
int progress
Definition: pgbench.c:274
#define PROC_IN_VACUUM
Definition: proc.h:55
#define PROC_IS_AUTOVACUUM
Definition: proc.h:54
VirtualTransactionId * GetCurrentVirtualXIDs(TransactionId limitXmin, bool excludeXmin0, bool allDbs, int excludeVacuum, int *nvxids)
Definition: procarray.c:3283
#define PROGRESS_WAITFOR_DONE
Definition: progress.h:115
#define PROGRESS_WAITFOR_TOTAL
Definition: progress.h:114
#define PROGRESS_WAITFOR_CURRENT_PID
Definition: progress.h:116
PGPROC * BackendIdGetProc(int backendID)
Definition: sinvaladt.c:385
Definition: proc.h:160
int pid
Definition: proc.h:184

References BackendIdGetProc(), GetCurrentVirtualXIDs(), i, j, pfree(), pgstat_progress_update_param(), PGPROC::pid, PROC_IN_SAFE_IC, PROC_IN_VACUUM, PROC_IS_AUTOVACUUM, progress, PROGRESS_WAITFOR_CURRENT_PID, PROGRESS_WAITFOR_DONE, PROGRESS_WAITFOR_TOTAL, SetInvalidVirtualTransactionId, VirtualTransactionIdEquals, VirtualTransactionIdIsValid, and VirtualXactLock().

Referenced by ATExecDetachPartitionFinalize(), DefineIndex(), and ReindexRelationConcurrently().