PostgreSQL Source Code git master
indexcmds.c File Reference
#include "postgres.h"
#include "access/amapi.h"
#include "access/gist.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/namespace.h"
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_database.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.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_oper.h"
#include "parser/parse_utilcmd.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 "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/injection_point.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 (const Datum *opts1, const Datum *opts2, int natts)
 
static void CheckPredicate (Expr *predicate)
 
static void ComputeIndexAttrs (IndexInfo *indexInfo, Oid *typeOids, Oid *collationOids, Oid *opclassOids, Datum *opclassOptions, int16 *colOptions, const List *attList, const List *exclusionOpNames, Oid relId, const char *accessMethodName, Oid accessMethodId, bool amcanorder, bool isconstraint, bool iswithoutoverlaps, Oid ddl_userid, int ddl_sec_context, int *ddl_save_nestlevel)
 
static char * ChooseIndexName (const char *tabname, Oid namespaceId, const List *colnames, const List *exclusionOpNames, bool primary, bool isconstraint)
 
static char * ChooseIndexNameAddition (const List *colnames)
 
static ListChooseIndexColumnNames (const List *indexElems)
 
static void ReindexIndex (const ReindexStmt *stmt, const ReindexParams *params, bool isTopLevel)
 
static void RangeVarCallbackForReindexIndex (const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
 
static Oid ReindexTable (const ReindexStmt *stmt, const ReindexParams *params, bool isTopLevel)
 
static void ReindexMultipleTables (const ReindexStmt *stmt, const ReindexParams *params)
 
static void reindex_error_callback (void *arg)
 
static void ReindexPartitions (const ReindexStmt *stmt, Oid relid, const ReindexParams *params, bool isTopLevel)
 
static void ReindexMultipleInternal (const ReindexStmt *stmt, const List *relids, const ReindexParams *params)
 
static bool ReindexRelationConcurrently (const ReindexStmt *stmt, Oid relationOid, const ReindexParams *params)
 
static void update_relispartition (Oid relationId, bool newval)
 
static void set_indexsafe_procflags (void)
 
bool CheckIndexCompatible (Oid oldId, const char *accessMethodName, const List *attributeList, const List *exclusionOpNames, bool isWithoutOverlaps)
 
void WaitForOlderSnapshots (TransactionId limitXmin, bool progress)
 
ObjectAddress DefineIndex (Oid tableId, IndexStmt *stmt, Oid indexRelationId, Oid parentIndexId, Oid parentConstraintId, int total_parts, bool is_alter_table, bool check_rights, bool check_not_in_use, bool skip_build, bool quiet)
 
Oid ResolveOpClass (const List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
 
Oid GetDefaultOpClass (Oid type_id, Oid am_id)
 
void GetOperatorFromCompareType (Oid opclass, Oid rhstype, CompareType cmptype, Oid *opid, StrategyNumber *strat)
 
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, const ReindexStmt *stmt, bool isTopLevel)
 
void IndexSetParentIndex (Relation partitionIdx, Oid parentOid)
 

Typedef Documentation

◆ ReindexErrorInfo

Function Documentation

◆ CheckIndexCompatible()

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

Definition at line 178 of file indexcmds.c.

183{
184 bool isconstraint;
185 Oid *typeIds;
186 Oid *collationIds;
187 Oid *opclassIds;
188 Datum *opclassOptions;
189 Oid accessMethodId;
190 Oid relationId;
191 HeapTuple tuple;
192 Form_pg_index indexForm;
193 Form_pg_am accessMethodForm;
194 IndexAmRoutine *amRoutine;
195 bool amcanorder;
196 bool amsummarizing;
197 int16 *coloptions;
198 IndexInfo *indexInfo;
199 int numberOfAttributes;
200 int old_natts;
201 bool ret = true;
202 oidvector *old_indclass;
203 oidvector *old_indcollation;
204 Relation irel;
205 int i;
206 Datum d;
207
208 /* Caller should already have the relation locked in some way. */
209 relationId = IndexGetRelation(oldId, false);
210
211 /*
212 * We can pretend isconstraint = false unconditionally. It only serves to
213 * decide the text of an error message that should never happen for us.
214 */
215 isconstraint = false;
216
217 numberOfAttributes = list_length(attributeList);
218 Assert(numberOfAttributes > 0);
219 Assert(numberOfAttributes <= INDEX_MAX_KEYS);
220
221 /* look up the access method */
222 tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
223 if (!HeapTupleIsValid(tuple))
225 (errcode(ERRCODE_UNDEFINED_OBJECT),
226 errmsg("access method \"%s\" does not exist",
227 accessMethodName)));
228 accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
229 accessMethodId = accessMethodForm->oid;
230 amRoutine = GetIndexAmRoutine(accessMethodForm->amhandler);
231 ReleaseSysCache(tuple);
232
233 amcanorder = amRoutine->amcanorder;
234 amsummarizing = amRoutine->amsummarizing;
235
236 /*
237 * Compute the operator classes, collations, and exclusion operators for
238 * the new index, so we can test whether it's compatible with the existing
239 * one. Note that ComputeIndexAttrs might fail here, but that's OK:
240 * DefineIndex would have failed later. Our attributeList contains only
241 * key attributes, thus we're filling ii_NumIndexAttrs and
242 * ii_NumIndexKeyAttrs with same value.
243 */
244 indexInfo = makeIndexInfo(numberOfAttributes, numberOfAttributes,
245 accessMethodId, NIL, NIL, false, false,
246 false, false, amsummarizing, isWithoutOverlaps);
247 typeIds = palloc_array(Oid, numberOfAttributes);
248 collationIds = palloc_array(Oid, numberOfAttributes);
249 opclassIds = palloc_array(Oid, numberOfAttributes);
250 opclassOptions = palloc_array(Datum, numberOfAttributes);
251 coloptions = palloc_array(int16, numberOfAttributes);
252 ComputeIndexAttrs(indexInfo,
253 typeIds, collationIds, opclassIds, opclassOptions,
254 coloptions, attributeList,
255 exclusionOpNames, relationId,
256 accessMethodName, accessMethodId,
257 amcanorder, isconstraint, isWithoutOverlaps, InvalidOid,
258 0, NULL);
259
260 /* Get the soon-obsolete pg_index tuple. */
261 tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(oldId));
262 if (!HeapTupleIsValid(tuple))
263 elog(ERROR, "cache lookup failed for index %u", oldId);
264 indexForm = (Form_pg_index) GETSTRUCT(tuple);
265
266 /*
267 * We don't assess expressions or predicates; assume incompatibility.
268 * Also, if the index is invalid for any reason, treat it as incompatible.
269 */
270 if (!(heap_attisnull(tuple, Anum_pg_index_indpred, NULL) &&
271 heap_attisnull(tuple, Anum_pg_index_indexprs, NULL) &&
272 indexForm->indisvalid))
273 {
274 ReleaseSysCache(tuple);
275 return false;
276 }
277
278 /* Any change in operator class or collation breaks compatibility. */
279 old_natts = indexForm->indnkeyatts;
280 Assert(old_natts == numberOfAttributes);
281
282 d = SysCacheGetAttrNotNull(INDEXRELID, tuple, Anum_pg_index_indcollation);
283 old_indcollation = (oidvector *) DatumGetPointer(d);
284
285 d = SysCacheGetAttrNotNull(INDEXRELID, tuple, Anum_pg_index_indclass);
286 old_indclass = (oidvector *) DatumGetPointer(d);
287
288 ret = (memcmp(old_indclass->values, opclassIds, old_natts * sizeof(Oid)) == 0 &&
289 memcmp(old_indcollation->values, collationIds, old_natts * sizeof(Oid)) == 0);
290
291 ReleaseSysCache(tuple);
292
293 if (!ret)
294 return false;
295
296 /* For polymorphic opcintype, column type changes break compatibility. */
297 irel = index_open(oldId, AccessShareLock); /* caller probably has a lock */
298 for (i = 0; i < old_natts; i++)
299 {
300 if (IsPolymorphicType(get_opclass_input_type(opclassIds[i])) &&
301 TupleDescAttr(irel->rd_att, i)->atttypid != typeIds[i])
302 {
303 ret = false;
304 break;
305 }
306 }
307
308 /* Any change in opclass options break compatibility. */
309 if (ret)
310 {
311 Datum *oldOpclassOptions = palloc_array(Datum, old_natts);
312
313 for (i = 0; i < old_natts; i++)
314 oldOpclassOptions[i] = get_attoptions(oldId, i + 1);
315
316 ret = CompareOpclassOptions(oldOpclassOptions, opclassOptions, old_natts);
317
318 pfree(oldOpclassOptions);
319 }
320
321 /* Any change in exclusion operator selections breaks compatibility. */
322 if (ret && indexInfo->ii_ExclusionOps != NULL)
323 {
324 Oid *old_operators,
325 *old_procs;
326 uint16 *old_strats;
327
328 RelationGetExclusionInfo(irel, &old_operators, &old_procs, &old_strats);
329 ret = memcmp(old_operators, indexInfo->ii_ExclusionOps,
330 old_natts * sizeof(Oid)) == 0;
331
332 /* Require an exact input type match for polymorphic operators. */
333 if (ret)
334 {
335 for (i = 0; i < old_natts && ret; i++)
336 {
337 Oid left,
338 right;
339
340 op_input_types(indexInfo->ii_ExclusionOps[i], &left, &right);
341 if ((IsPolymorphicType(left) || IsPolymorphicType(right)) &&
342 TupleDescAttr(irel->rd_att, i)->atttypid != typeIds[i])
343 {
344 ret = false;
345 break;
346 }
347 }
348 }
349 }
350
351 index_close(irel, NoLock);
352 return ret;
353}
IndexAmRoutine * GetIndexAmRoutine(Oid amhandler)
Definition: amapi.c:33
int16_t int16
Definition: c.h:497
uint16_t uint16
Definition: c.h:501
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
#define palloc_array(type, count)
Definition: fe_memutils.h:76
Assert(PointerIsAligned(start, uint64))
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition: heaptuple.c:456
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition: index.c:3583
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
static void ComputeIndexAttrs(IndexInfo *indexInfo, Oid *typeOids, Oid *collationOids, Oid *opclassOids, Datum *opclassOptions, int16 *colOptions, const List *attList, const List *exclusionOpNames, Oid relId, const char *accessMethodName, Oid accessMethodId, bool amcanorder, bool isconstraint, bool iswithoutoverlaps, Oid ddl_userid, int ddl_sec_context, int *ddl_save_nestlevel)
Definition: indexcmds.c:1868
static bool CompareOpclassOptions(const Datum *opts1, const Datum *opts2, int natts)
Definition: indexcmds.c:362
int i
Definition: isn.c:74
#define NoLock
Definition: lockdefs.h:34
#define AccessShareLock
Definition: lockdefs.h:36
Oid get_opclass_input_type(Oid opclass)
Definition: lsyscache.c:1247
Datum get_attoptions(Oid relid, int16 attnum)
Definition: lsyscache.c:1005
void op_input_types(Oid opno, Oid *lefttype, Oid *righttype)
Definition: lsyscache.c:1441
IndexInfo * makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions, List *predicates, bool unique, bool nulls_not_distinct, bool isready, bool concurrent, bool summarizing, bool withoutoverlaps)
Definition: makefuncs.c:834
void pfree(void *pointer)
Definition: mcxt.c:1524
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:152
#define NIL
Definition: pg_list.h:68
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
uintptr_t Datum
Definition: postgres.h:69
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:257
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
#define InvalidOid
Definition: postgres_ext.h:37
unsigned int Oid
Definition: postgres_ext.h:32
void RelationGetExclusionInfo(Relation indexRelation, Oid **operators, Oid **procs, uint16 **strategies)
Definition: relcache.c:5581
bool amsummarizing
Definition: amapi.h:279
bool amcanorder
Definition: amapi.h:243
Oid * ii_ExclusionOps
Definition: execnodes.h:203
TupleDesc rd_att
Definition: rel.h:112
Definition: c.h:697
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:704
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:631
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:154

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

Referenced by TryReuseIndex().

◆ CheckPredicate()

static void CheckPredicate ( Expr predicate)
static

Definition at line 1841 of file indexcmds.c.

1842{
1843 /*
1844 * transformExpr() should have already rejected subqueries, aggregates,
1845 * and window functions, based on the EXPR_KIND_ for a predicate.
1846 */
1847
1848 /*
1849 * A predicate using mutable functions is probably wrong, for the same
1850 * reasons that we don't allow an index expression to use one.
1851 */
1853 ereport(ERROR,
1854 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1855 errmsg("functions in index predicate must be marked IMMUTABLE")));
1856}
bool contain_mutable_functions_after_planning(Expr *expr)
Definition: clauses.c:489

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

Referenced by DefineIndex().

◆ ChooseIndexColumnNames()

static List * ChooseIndexColumnNames ( const List indexElems)
static

Definition at line 2727 of file indexcmds.c.

2728{
2729 List *result = NIL;
2730 ListCell *lc;
2731
2732 foreach(lc, indexElems)
2733 {
2734 IndexElem *ielem = (IndexElem *) lfirst(lc);
2735 const char *origname;
2736 const char *curname;
2737 int i;
2738 char buf[NAMEDATALEN];
2739
2740 /* Get the preliminary name from the IndexElem */
2741 if (ielem->indexcolname)
2742 origname = ielem->indexcolname; /* caller-specified name */
2743 else if (ielem->name)
2744 origname = ielem->name; /* simple column reference */
2745 else
2746 origname = "expr"; /* default name for expression */
2747
2748 /* If it conflicts with any previous column, tweak it */
2749 curname = origname;
2750 for (i = 1;; i++)
2751 {
2752 ListCell *lc2;
2753 char nbuf[32];
2754 int nlen;
2755
2756 foreach(lc2, result)
2757 {
2758 if (strcmp(curname, (char *) lfirst(lc2)) == 0)
2759 break;
2760 }
2761 if (lc2 == NULL)
2762 break; /* found nonconflicting name */
2763
2764 sprintf(nbuf, "%d", i);
2765
2766 /* Ensure generated names are shorter than NAMEDATALEN */
2767 nlen = pg_mbcliplen(origname, strlen(origname),
2768 NAMEDATALEN - 1 - strlen(nbuf));
2769 memcpy(buf, origname, nlen);
2770 strcpy(buf + nlen, nbuf);
2771 curname = buf;
2772 }
2773
2774 /* And attach to the result list */
2775 result = lappend(result, pstrdup(curname));
2776 }
2777 return result;
2778}
List * lappend(List *list, void *datum)
Definition: list.c:339
int pg_mbcliplen(const char *mbstr, int len, int limit)
Definition: mbutils.c:1083
char * pstrdup(const char *in)
Definition: mcxt.c:1699
#define NAMEDATALEN
#define lfirst(lc)
Definition: pg_list.h:172
static char * buf
Definition: pg_test_fsync.c:72
#define sprintf
Definition: port.h:241
char * indexcolname
Definition: parsenodes.h:796
char * name
Definition: parsenodes.h:794
Definition: pg_list.h:54

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,
const List colnames,
const List exclusionOpNames,
bool  primary,
bool  isconstraint 
)
static

Definition at line 2638 of file indexcmds.c.

2641{
2642 char *indexname;
2643
2644 if (primary)
2645 {
2646 /* the primary key's name does not depend on the specific column(s) */
2647 indexname = ChooseRelationName(tabname,
2648 NULL,
2649 "pkey",
2650 namespaceId,
2651 true);
2652 }
2653 else if (exclusionOpNames != NIL)
2654 {
2655 indexname = ChooseRelationName(tabname,
2656 ChooseIndexNameAddition(colnames),
2657 "excl",
2658 namespaceId,
2659 true);
2660 }
2661 else if (isconstraint)
2662 {
2663 indexname = ChooseRelationName(tabname,
2664 ChooseIndexNameAddition(colnames),
2665 "key",
2666 namespaceId,
2667 true);
2668 }
2669 else
2670 {
2671 indexname = ChooseRelationName(tabname,
2672 ChooseIndexNameAddition(colnames),
2673 "idx",
2674 namespaceId,
2675 false);
2676 }
2677
2678 return indexname;
2679}
char * ChooseRelationName(const char *name1, const char *name2, const char *label, Oid namespaceid, bool isconstraint)
Definition: indexcmds.c:2602
static char * ChooseIndexNameAddition(const List *colnames)
Definition: indexcmds.c:2693

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

Referenced by DefineIndex().

◆ ChooseIndexNameAddition()

static char * ChooseIndexNameAddition ( const List colnames)
static

Definition at line 2693 of file indexcmds.c.

2694{
2695 char buf[NAMEDATALEN * 2];
2696 int buflen = 0;
2697 ListCell *lc;
2698
2699 buf[0] = '\0';
2700 foreach(lc, colnames)
2701 {
2702 const char *name = (const char *) lfirst(lc);
2703
2704 if (buflen > 0)
2705 buf[buflen++] = '_'; /* insert _ between names */
2706
2707 /*
2708 * At this point we have buflen <= NAMEDATALEN. name should be less
2709 * than NAMEDATALEN already, but use strlcpy for paranoia.
2710 */
2711 strlcpy(buf + buflen, name, NAMEDATALEN);
2712 buflen += strlen(buf + buflen);
2713 if (buflen >= NAMEDATALEN)
2714 break;
2715 }
2716 return pstrdup(buf);
2717}
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
const char * name

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 2602 of file indexcmds.c.

2605{
2606 int pass = 0;
2607 char *relname = NULL;
2608 char modlabel[NAMEDATALEN];
2609
2610 /* try the unmodified label first */
2611 strlcpy(modlabel, label, sizeof(modlabel));
2612
2613 for (;;)
2614 {
2615 relname = makeObjectName(name1, name2, modlabel);
2616
2617 if (!OidIsValid(get_relname_relid(relname, namespaceid)))
2618 {
2619 if (!isconstraint ||
2620 !ConstraintNameExists(relname, namespaceid))
2621 break;
2622 }
2623
2624 /* found a conflict, so try a new name component */
2625 pfree(relname);
2626 snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
2627 }
2628
2629 return relname;
2630}
#define OidIsValid(objectId)
Definition: c.h:746
char * makeObjectName(const char *name1, const char *name2, const char *label)
Definition: indexcmds.c:2516
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:1968
static char * label
NameData relname
Definition: pg_class.h:38
bool ConstraintNameExists(const char *conname, Oid namespaceid)
#define snprintf
Definition: port.h:239

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

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

◆ CompareOpclassOptions()

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

Definition at line 362 of file indexcmds.c.

363{
364 int i;
365 FmgrInfo fm;
366
367 if (!opts1 && !opts2)
368 return true;
369
370 fmgr_info(F_ARRAY_EQ, &fm);
371 for (i = 0; i < natts; i++)
372 {
373 Datum opt1 = opts1 ? opts1[i] : (Datum) 0;
374 Datum opt2 = opts2 ? opts2[i] : (Datum) 0;
375
376 if (opt1 == (Datum) 0)
377 {
378 if (opt2 == (Datum) 0)
379 continue;
380 else
381 return false;
382 }
383 else if (opt2 == (Datum) 0)
384 return false;
385
386 /*
387 * Compare non-NULL text[] datums. Use C collation to enforce binary
388 * equivalence of texts, because we don't know anything about the
389 * semantics of opclass options.
390 */
391 if (!DatumGetBool(FunctionCall2Coll(&fm, C_COLLATION_OID, opt1, opt2)))
392 return false;
393 }
394
395 return true;
396}
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1149
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:127
static bool DatumGetBool(Datum X)
Definition: postgres.h:95
Definition: fmgr.h:57

References DatumGetBool(), fmgr_info(), FunctionCall2Coll(), and i.

Referenced by CheckIndexCompatible().

◆ ComputeIndexAttrs()

static void ComputeIndexAttrs ( IndexInfo indexInfo,
Oid typeOids,
Oid collationOids,
Oid opclassOids,
Datum opclassOptions,
int16 colOptions,
const List attList,
const List exclusionOpNames,
Oid  relId,
const char *  accessMethodName,
Oid  accessMethodId,
bool  amcanorder,
bool  isconstraint,
bool  iswithoutoverlaps,
Oid  ddl_userid,
int  ddl_sec_context,
int *  ddl_save_nestlevel 
)
static

Definition at line 1868 of file indexcmds.c.

1885{
1886 ListCell *nextExclOp;
1887 ListCell *lc;
1888 int attn;
1889 int nkeycols = indexInfo->ii_NumIndexKeyAttrs;
1890 Oid save_userid;
1891 int save_sec_context;
1892
1893 /* Allocate space for exclusion operator info, if needed */
1894 if (exclusionOpNames)
1895 {
1896 Assert(list_length(exclusionOpNames) == nkeycols);
1897 indexInfo->ii_ExclusionOps = palloc_array(Oid, nkeycols);
1898 indexInfo->ii_ExclusionProcs = palloc_array(Oid, nkeycols);
1899 indexInfo->ii_ExclusionStrats = palloc_array(uint16, nkeycols);
1900 nextExclOp = list_head(exclusionOpNames);
1901 }
1902 else
1903 nextExclOp = NULL;
1904
1905 /*
1906 * If this is a WITHOUT OVERLAPS constraint, we need space for exclusion
1907 * ops, but we don't need to parse anything, so we can let nextExclOp be
1908 * NULL. Note that for partitions/inheriting/LIKE, exclusionOpNames will
1909 * be set, so we already allocated above.
1910 */
1911 if (iswithoutoverlaps)
1912 {
1913 if (exclusionOpNames == NIL)
1914 {
1915 indexInfo->ii_ExclusionOps = palloc_array(Oid, nkeycols);
1916 indexInfo->ii_ExclusionProcs = palloc_array(Oid, nkeycols);
1917 indexInfo->ii_ExclusionStrats = palloc_array(uint16, nkeycols);
1918 }
1919 nextExclOp = NULL;
1920 }
1921
1922 if (OidIsValid(ddl_userid))
1923 GetUserIdAndSecContext(&save_userid, &save_sec_context);
1924
1925 /*
1926 * process attributeList
1927 */
1928 attn = 0;
1929 foreach(lc, attList)
1930 {
1931 IndexElem *attribute = (IndexElem *) lfirst(lc);
1932 Oid atttype;
1933 Oid attcollation;
1934
1935 /*
1936 * Process the column-or-expression to be indexed.
1937 */
1938 if (attribute->name != NULL)
1939 {
1940 /* Simple index attribute */
1941 HeapTuple atttuple;
1942 Form_pg_attribute attform;
1943
1944 Assert(attribute->expr == NULL);
1945 atttuple = SearchSysCacheAttName(relId, attribute->name);
1946 if (!HeapTupleIsValid(atttuple))
1947 {
1948 /* difference in error message spellings is historical */
1949 if (isconstraint)
1950 ereport(ERROR,
1951 (errcode(ERRCODE_UNDEFINED_COLUMN),
1952 errmsg("column \"%s\" named in key does not exist",
1953 attribute->name)));
1954 else
1955 ereport(ERROR,
1956 (errcode(ERRCODE_UNDEFINED_COLUMN),
1957 errmsg("column \"%s\" does not exist",
1958 attribute->name)));
1959 }
1960 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
1961 indexInfo->ii_IndexAttrNumbers[attn] = attform->attnum;
1962 atttype = attform->atttypid;
1963 attcollation = attform->attcollation;
1964 ReleaseSysCache(atttuple);
1965 }
1966 else
1967 {
1968 /* Index expression */
1969 Node *expr = attribute->expr;
1970
1971 Assert(expr != NULL);
1972
1973 if (attn >= nkeycols)
1974 ereport(ERROR,
1975 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1976 errmsg("expressions are not supported in included columns")));
1977 atttype = exprType(expr);
1978 attcollation = exprCollation(expr);
1979
1980 /*
1981 * Strip any top-level COLLATE clause. This ensures that we treat
1982 * "x COLLATE y" and "(x COLLATE y)" alike.
1983 */
1984 while (IsA(expr, CollateExpr))
1985 expr = (Node *) ((CollateExpr *) expr)->arg;
1986
1987 if (IsA(expr, Var) &&
1988 ((Var *) expr)->varattno != InvalidAttrNumber)
1989 {
1990 /*
1991 * User wrote "(column)" or "(column COLLATE something)".
1992 * Treat it like simple attribute anyway.
1993 */
1994 indexInfo->ii_IndexAttrNumbers[attn] = ((Var *) expr)->varattno;
1995 }
1996 else
1997 {
1998 indexInfo->ii_IndexAttrNumbers[attn] = 0; /* marks expression */
1999 indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions,
2000 expr);
2001
2002 /*
2003 * transformExpr() should have already rejected subqueries,
2004 * aggregates, and window functions, based on the EXPR_KIND_
2005 * for an index expression.
2006 */
2007
2008 /*
2009 * An expression using mutable functions is probably wrong,
2010 * since if you aren't going to get the same result for the
2011 * same data every time, it's not clear what the index entries
2012 * mean at all.
2013 */
2015 ereport(ERROR,
2016 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2017 errmsg("functions in index expression must be marked IMMUTABLE")));
2018 }
2019 }
2020
2021 typeOids[attn] = atttype;
2022
2023 /*
2024 * Included columns have no collation, no opclass and no ordering
2025 * options.
2026 */
2027 if (attn >= nkeycols)
2028 {
2029 if (attribute->collation)
2030 ereport(ERROR,
2031 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2032 errmsg("including column does not support a collation")));
2033 if (attribute->opclass)
2034 ereport(ERROR,
2035 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2036 errmsg("including column does not support an operator class")));
2037 if (attribute->ordering != SORTBY_DEFAULT)
2038 ereport(ERROR,
2039 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2040 errmsg("including column does not support ASC/DESC options")));
2041 if (attribute->nulls_ordering != SORTBY_NULLS_DEFAULT)
2042 ereport(ERROR,
2043 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2044 errmsg("including column does not support NULLS FIRST/LAST options")));
2045
2046 opclassOids[attn] = InvalidOid;
2047 opclassOptions[attn] = (Datum) 0;
2048 colOptions[attn] = 0;
2049 collationOids[attn] = InvalidOid;
2050 attn++;
2051
2052 continue;
2053 }
2054
2055 /*
2056 * Apply collation override if any. Use of ddl_userid is necessary
2057 * due to ACL checks therein, and it's safe because collations don't
2058 * contain opaque expressions (or non-opaque expressions).
2059 */
2060 if (attribute->collation)
2061 {
2062 if (OidIsValid(ddl_userid))
2063 {
2064 AtEOXact_GUC(false, *ddl_save_nestlevel);
2065 SetUserIdAndSecContext(ddl_userid, ddl_sec_context);
2066 }
2067 attcollation = get_collation_oid(attribute->collation, false);
2068 if (OidIsValid(ddl_userid))
2069 {
2070 SetUserIdAndSecContext(save_userid, save_sec_context);
2071 *ddl_save_nestlevel = NewGUCNestLevel();
2073 }
2074 }
2075
2076 /*
2077 * Check we have a collation iff it's a collatable type. The only
2078 * expected failures here are (1) COLLATE applied to a noncollatable
2079 * type, or (2) index expression had an unresolved collation. But we
2080 * might as well code this to be a complete consistency check.
2081 */
2082 if (type_is_collatable(atttype))
2083 {
2084 if (!OidIsValid(attcollation))
2085 ereport(ERROR,
2086 (errcode(ERRCODE_INDETERMINATE_COLLATION),
2087 errmsg("could not determine which collation to use for index expression"),
2088 errhint("Use the COLLATE clause to set the collation explicitly.")));
2089 }
2090 else
2091 {
2092 if (OidIsValid(attcollation))
2093 ereport(ERROR,
2094 (errcode(ERRCODE_DATATYPE_MISMATCH),
2095 errmsg("collations are not supported by type %s",
2096 format_type_be(atttype))));
2097 }
2098
2099 collationOids[attn] = attcollation;
2100
2101 /*
2102 * Identify the opclass to use. Use of ddl_userid is necessary due to
2103 * ACL checks therein. This is safe despite opclasses containing
2104 * opaque expressions (specifically, functions), because only
2105 * superusers can define opclasses.
2106 */
2107 if (OidIsValid(ddl_userid))
2108 {
2109 AtEOXact_GUC(false, *ddl_save_nestlevel);
2110 SetUserIdAndSecContext(ddl_userid, ddl_sec_context);
2111 }
2112 opclassOids[attn] = ResolveOpClass(attribute->opclass,
2113 atttype,
2114 accessMethodName,
2115 accessMethodId);
2116 if (OidIsValid(ddl_userid))
2117 {
2118 SetUserIdAndSecContext(save_userid, save_sec_context);
2119 *ddl_save_nestlevel = NewGUCNestLevel();
2121 }
2122
2123 /*
2124 * Identify the exclusion operator, if any.
2125 */
2126 if (nextExclOp)
2127 {
2128 List *opname = (List *) lfirst(nextExclOp);
2129 Oid opid;
2130 Oid opfamily;
2131 int strat;
2132
2133 /*
2134 * Find the operator --- it must accept the column datatype
2135 * without runtime coercion (but binary compatibility is OK).
2136 * Operators contain opaque expressions (specifically, functions).
2137 * compatible_oper_opid() boils down to oper() and
2138 * IsBinaryCoercible(). PostgreSQL would have security problems
2139 * elsewhere if oper() started calling opaque expressions.
2140 */
2141 if (OidIsValid(ddl_userid))
2142 {
2143 AtEOXact_GUC(false, *ddl_save_nestlevel);
2144 SetUserIdAndSecContext(ddl_userid, ddl_sec_context);
2145 }
2146 opid = compatible_oper_opid(opname, atttype, atttype, false);
2147 if (OidIsValid(ddl_userid))
2148 {
2149 SetUserIdAndSecContext(save_userid, save_sec_context);
2150 *ddl_save_nestlevel = NewGUCNestLevel();
2152 }
2153
2154 /*
2155 * Only allow commutative operators to be used in exclusion
2156 * constraints. If X conflicts with Y, but Y does not conflict
2157 * with X, bad things will happen.
2158 */
2159 if (get_commutator(opid) != opid)
2160 ereport(ERROR,
2161 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2162 errmsg("operator %s is not commutative",
2163 format_operator(opid)),
2164 errdetail("Only commutative operators can be used in exclusion constraints.")));
2165
2166 /*
2167 * Operator must be a member of the right opfamily, too
2168 */
2169 opfamily = get_opclass_family(opclassOids[attn]);
2170 strat = get_op_opfamily_strategy(opid, opfamily);
2171 if (strat == 0)
2172 ereport(ERROR,
2173 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2174 errmsg("operator %s is not a member of operator family \"%s\"",
2175 format_operator(opid),
2176 get_opfamily_name(opfamily, false)),
2177 errdetail("The exclusion operator must be related to the index operator class for the constraint.")));
2178
2179 indexInfo->ii_ExclusionOps[attn] = opid;
2180 indexInfo->ii_ExclusionProcs[attn] = get_opcode(opid);
2181 indexInfo->ii_ExclusionStrats[attn] = strat;
2182 nextExclOp = lnext(exclusionOpNames, nextExclOp);
2183 }
2184 else if (iswithoutoverlaps)
2185 {
2186 CompareType cmptype;
2187 StrategyNumber strat;
2188 Oid opid;
2189
2190 if (attn == nkeycols - 1)
2191 cmptype = COMPARE_OVERLAP;
2192 else
2193 cmptype = COMPARE_EQ;
2194 GetOperatorFromCompareType(opclassOids[attn], InvalidOid, cmptype, &opid, &strat);
2195 indexInfo->ii_ExclusionOps[attn] = opid;
2196 indexInfo->ii_ExclusionProcs[attn] = get_opcode(opid);
2197 indexInfo->ii_ExclusionStrats[attn] = strat;
2198 }
2199
2200 /*
2201 * Set up the per-column options (indoption field). For now, this is
2202 * zero for any un-ordered index, while ordered indexes have DESC and
2203 * NULLS FIRST/LAST options.
2204 */
2205 colOptions[attn] = 0;
2206 if (amcanorder)
2207 {
2208 /* default ordering is ASC */
2209 if (attribute->ordering == SORTBY_DESC)
2210 colOptions[attn] |= INDOPTION_DESC;
2211 /* default null ordering is LAST for ASC, FIRST for DESC */
2212 if (attribute->nulls_ordering == SORTBY_NULLS_DEFAULT)
2213 {
2214 if (attribute->ordering == SORTBY_DESC)
2215 colOptions[attn] |= INDOPTION_NULLS_FIRST;
2216 }
2217 else if (attribute->nulls_ordering == SORTBY_NULLS_FIRST)
2218 colOptions[attn] |= INDOPTION_NULLS_FIRST;
2219 }
2220 else
2221 {
2222 /* index AM does not support ordering */
2223 if (attribute->ordering != SORTBY_DEFAULT)
2224 ereport(ERROR,
2225 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2226 errmsg("access method \"%s\" does not support ASC/DESC options",
2227 accessMethodName)));
2228 if (attribute->nulls_ordering != SORTBY_NULLS_DEFAULT)
2229 ereport(ERROR,
2230 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2231 errmsg("access method \"%s\" does not support NULLS FIRST/LAST options",
2232 accessMethodName)));
2233 }
2234
2235 /* Set up the per-column opclass options (attoptions field). */
2236 if (attribute->opclassopts)
2237 {
2238 Assert(attn < nkeycols);
2239
2240 opclassOptions[attn] =
2241 transformRelOptions((Datum) 0, attribute->opclassopts,
2242 NULL, NULL, false, false);
2243 }
2244 else
2245 opclassOptions[attn] = (Datum) 0;
2246
2247 attn++;
2248 }
2249}
#define InvalidAttrNumber
Definition: attnum.h:23
CompareType
Definition: cmptype.h:32
@ COMPARE_OVERLAP
Definition: cmptype.h:40
@ COMPARE_EQ
Definition: cmptype.h:36
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errhint(const char *fmt,...)
Definition: elog.c:1317
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
int NewGUCNestLevel(void)
Definition: guc.c:2235
void RestrictSearchPath(void)
Definition: guc.c:2246
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2262
Oid ResolveOpClass(const List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
Definition: indexcmds.c:2258
void GetOperatorFromCompareType(Oid opclass, Oid rhstype, CompareType cmptype, Oid *opid, StrategyNumber *strat)
Definition: indexcmds.c:2445
Oid get_opclass_family(Oid opclass)
Definition: lsyscache.c:1225
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1368
int get_op_opfamily_strategy(Oid opno, Oid opfamily)
Definition: lsyscache.c:84
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition: lsyscache.c:1336
bool type_is_collatable(Oid typid)
Definition: lsyscache.c:3164
Oid get_commutator(Oid opno)
Definition: lsyscache.c:1592
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:663
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:670
Oid get_collation_oid(List *collname, bool missing_ok)
Definition: namespace.c:3971
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:821
#define IsA(nodeptr, _type_)
Definition: nodes.h:160
Oid compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError)
Definition: parse_oper.c:487
@ SORTBY_NULLS_DEFAULT
Definition: parsenodes.h:54
@ SORTBY_NULLS_FIRST
Definition: parsenodes.h:55
@ SORTBY_DESC
Definition: parsenodes.h:48
@ SORTBY_DEFAULT
Definition: parsenodes.h:46
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
void * arg
static ListCell * list_head(const List *l)
Definition: pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
char * format_operator(Oid operator_oid)
Definition: regproc.c:793
Datum transformRelOptions(Datum oldOptions, List *defList, const char *namspace, const char *const validnsps[], bool acceptOidsOff, bool isReset)
Definition: reloptions.c:1175
uint16 StrategyNumber
Definition: stratnum.h:22
Node * expr
Definition: parsenodes.h:795
SortByDir ordering
Definition: parsenodes.h:800
List * opclassopts
Definition: parsenodes.h:799
SortByNulls nulls_ordering
Definition: parsenodes.h:801
List * opclass
Definition: parsenodes.h:798
List * collation
Definition: parsenodes.h:797
uint16 * ii_ExclusionStrats
Definition: execnodes.h:205
int ii_NumIndexKeyAttrs
Definition: execnodes.h:197
List * ii_Expressions
Definition: execnodes.h:199
Oid * ii_ExclusionProcs
Definition: execnodes.h:204
AttrNumber ii_IndexAttrNumbers[INDEX_MAX_KEYS]
Definition: execnodes.h:198
Definition: nodes.h:131
Definition: primnodes.h:262
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
Definition: syscache.c:480

References arg, Assert(), AtEOXact_GUC(), IndexElem::collation, COMPARE_EQ, COMPARE_OVERLAP, compatible_oper_opid(), contain_mutable_functions_after_planning(), 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(), get_opfamily_name(), GetOperatorFromCompareType(), GETSTRUCT(), GetUserIdAndSecContext(), HeapTupleIsValid, IndexInfo::ii_ExclusionOps, IndexInfo::ii_ExclusionProcs, IndexInfo::ii_ExclusionStrats, IndexInfo::ii_Expressions, IndexInfo::ii_IndexAttrNumbers, IndexInfo::ii_NumIndexKeyAttrs, InvalidAttrNumber, InvalidOid, IsA, lappend(), lfirst, list_head(), list_length(), lnext(), IndexElem::name, NewGUCNestLevel(), NIL, IndexElem::nulls_ordering, OidIsValid, IndexElem::opclass, IndexElem::opclassopts, IndexElem::ordering, palloc_array, ReleaseSysCache(), ResolveOpClass(), RestrictSearchPath(), 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  tableId,
IndexStmt stmt,
Oid  indexRelationId,
Oid  parentIndexId,
Oid  parentConstraintId,
int  total_parts,
bool  is_alter_table,
bool  check_rights,
bool  check_not_in_use,
bool  skip_build,
bool  quiet 
)

Definition at line 542 of file indexcmds.c.

553{
554 bool concurrent;
555 char *indexRelationName;
556 char *accessMethodName;
557 Oid *typeIds;
558 Oid *collationIds;
559 Oid *opclassIds;
560 Datum *opclassOptions;
561 Oid accessMethodId;
562 Oid namespaceId;
563 Oid tablespaceId;
564 Oid createdConstraintId = InvalidOid;
565 List *indexColNames;
566 List *allIndexParams;
567 Relation rel;
568 HeapTuple tuple;
569 Form_pg_am accessMethodForm;
570 IndexAmRoutine *amRoutine;
571 bool amcanorder;
572 bool amissummarizing;
573 amoptions_function amoptions;
574 bool exclusion;
575 bool partitioned;
576 bool safe_index;
577 Datum reloptions;
578 int16 *coloptions;
579 IndexInfo *indexInfo;
580 bits16 flags;
581 bits16 constr_flags;
582 int numberOfAttributes;
583 int numberOfKeyAttributes;
584 TransactionId limitXmin;
585 ObjectAddress address;
586 LockRelId heaprelid;
587 LOCKTAG heaplocktag;
588 LOCKMODE lockmode;
589 Snapshot snapshot;
590 Oid root_save_userid;
591 int root_save_sec_context;
592 int root_save_nestlevel;
593
594 root_save_nestlevel = NewGUCNestLevel();
595
597
598 /*
599 * Some callers need us to run with an empty default_tablespace; this is a
600 * necessary hack to be able to reproduce catalog state accurately when
601 * recreating indexes after table-rewriting ALTER TABLE.
602 */
603 if (stmt->reset_default_tblspc)
604 (void) set_config_option("default_tablespace", "",
606 GUC_ACTION_SAVE, true, 0, false);
607
608 /*
609 * Force non-concurrent build on temporary relations, even if CONCURRENTLY
610 * was requested. Other backends can't access a temporary relation, so
611 * there's no harm in grabbing a stronger lock, and a non-concurrent DROP
612 * is more efficient. Do this before any use of the concurrent option is
613 * done.
614 */
615 if (stmt->concurrent && get_rel_persistence(tableId) != RELPERSISTENCE_TEMP)
616 concurrent = true;
617 else
618 concurrent = false;
619
620 /*
621 * Start progress report. If we're building a partition, this was already
622 * done.
623 */
624 if (!OidIsValid(parentIndexId))
625 {
628 concurrent ?
631 }
632
633 /*
634 * No index OID to report yet
635 */
637 InvalidOid);
638
639 /*
640 * count key attributes in index
641 */
642 numberOfKeyAttributes = list_length(stmt->indexParams);
643
644 /*
645 * Calculate the new list of index columns including both key columns and
646 * INCLUDE columns. Later we can determine which of these are key
647 * columns, and which are just part of the INCLUDE list by checking the
648 * list position. A list item in a position less than ii_NumIndexKeyAttrs
649 * is part of the key columns, and anything equal to and over is part of
650 * the INCLUDE columns.
651 */
652 allIndexParams = list_concat_copy(stmt->indexParams,
653 stmt->indexIncludingParams);
654 numberOfAttributes = list_length(allIndexParams);
655
656 if (numberOfKeyAttributes <= 0)
658 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
659 errmsg("must specify at least one column")));
660 if (numberOfAttributes > INDEX_MAX_KEYS)
662 (errcode(ERRCODE_TOO_MANY_COLUMNS),
663 errmsg("cannot use more than %d columns in an index",
665
666 /*
667 * Only SELECT ... FOR UPDATE/SHARE are allowed while doing a standard
668 * index build; but for concurrent builds we allow INSERT/UPDATE/DELETE
669 * (but not VACUUM).
670 *
671 * NB: Caller is responsible for making sure that tableId refers to the
672 * relation on which the index should be built; except in bootstrap mode,
673 * this will typically require the caller to have already locked the
674 * relation. To avoid lock upgrade hazards, that lock should be at least
675 * as strong as the one we take here.
676 *
677 * NB: If the lock strength here ever changes, code that is run by
678 * parallel workers under the control of certain particular ambuild
679 * functions will need to be updated, too.
680 */
681 lockmode = concurrent ? ShareUpdateExclusiveLock : ShareLock;
682 rel = table_open(tableId, lockmode);
683
684 /*
685 * Switch to the table owner's userid, so that any index functions are run
686 * as that user. Also lock down security-restricted operations. We
687 * already arranged to make GUC variable changes local to this command.
688 */
689 GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context);
690 SetUserIdAndSecContext(rel->rd_rel->relowner,
691 root_save_sec_context | SECURITY_RESTRICTED_OPERATION);
692
693 namespaceId = RelationGetNamespace(rel);
694
695 /*
696 * It has exclusion constraint behavior if it's an EXCLUDE constraint or a
697 * temporal PRIMARY KEY/UNIQUE constraint
698 */
699 exclusion = stmt->excludeOpNames || stmt->iswithoutoverlaps;
700
701 /* Ensure that it makes sense to index this kind of relation */
702 switch (rel->rd_rel->relkind)
703 {
704 case RELKIND_RELATION:
705 case RELKIND_MATVIEW:
706 case RELKIND_PARTITIONED_TABLE:
707 /* OK */
708 break;
709 default:
711 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
712 errmsg("cannot create index on relation \"%s\"",
715 break;
716 }
717
718 /*
719 * Establish behavior for partitioned tables, and verify sanity of
720 * parameters.
721 *
722 * We do not build an actual index in this case; we only create a few
723 * catalog entries. The actual indexes are built by recursing for each
724 * partition.
725 */
726 partitioned = rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE;
727 if (partitioned)
728 {
729 /*
730 * Note: we check 'stmt->concurrent' rather than 'concurrent', so that
731 * the error is thrown also for temporary tables. Seems better to be
732 * consistent, even though we could do it on temporary table because
733 * we're not actually doing it concurrently.
734 */
735 if (stmt->concurrent)
737 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
738 errmsg("cannot create index on partitioned table \"%s\" concurrently",
740 }
741
742 /*
743 * Don't try to CREATE INDEX on temp tables of other backends.
744 */
745 if (RELATION_IS_OTHER_TEMP(rel))
747 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
748 errmsg("cannot create indexes on temporary tables of other sessions")));
749
750 /*
751 * Unless our caller vouches for having checked this already, insist that
752 * the table not be in use by our own session, either. Otherwise we might
753 * fail to make entries in the new index (for instance, if an INSERT or
754 * UPDATE is in progress and has already made its list of target indexes).
755 */
756 if (check_not_in_use)
757 CheckTableNotInUse(rel, "CREATE INDEX");
758
759 /*
760 * Verify we (still) have CREATE rights in the rel's namespace.
761 * (Presumably we did when the rel was created, but maybe not anymore.)
762 * Skip check if caller doesn't want it. Also skip check if
763 * bootstrapping, since permissions machinery may not be working yet.
764 */
765 if (check_rights && !IsBootstrapProcessingMode())
766 {
767 AclResult aclresult;
768
769 aclresult = object_aclcheck(NamespaceRelationId, namespaceId, root_save_userid,
770 ACL_CREATE);
771 if (aclresult != ACLCHECK_OK)
772 aclcheck_error(aclresult, OBJECT_SCHEMA,
773 get_namespace_name(namespaceId));
774 }
775
776 /*
777 * Select tablespace to use. If not specified, use default tablespace
778 * (which may in turn default to database's default).
779 */
780 if (stmt->tableSpace)
781 {
782 tablespaceId = get_tablespace_oid(stmt->tableSpace, false);
783 if (partitioned && tablespaceId == MyDatabaseTableSpace)
785 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
786 errmsg("cannot specify default tablespace for partitioned relations")));
787 }
788 else
789 {
790 tablespaceId = GetDefaultTablespace(rel->rd_rel->relpersistence,
791 partitioned);
792 /* note InvalidOid is OK in this case */
793 }
794
795 /* Check tablespace permissions */
796 if (check_rights &&
797 OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
798 {
799 AclResult aclresult;
800
801 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, root_save_userid,
802 ACL_CREATE);
803 if (aclresult != ACLCHECK_OK)
805 get_tablespace_name(tablespaceId));
806 }
807
808 /*
809 * Force shared indexes into the pg_global tablespace. This is a bit of a
810 * hack but seems simpler than marking them in the BKI commands. On the
811 * other hand, if it's not shared, don't allow it to be placed there.
812 */
813 if (rel->rd_rel->relisshared)
814 tablespaceId = GLOBALTABLESPACE_OID;
815 else if (tablespaceId == GLOBALTABLESPACE_OID)
817 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
818 errmsg("only shared relations can be placed in pg_global tablespace")));
819
820 /*
821 * Choose the index column names.
822 */
823 indexColNames = ChooseIndexColumnNames(allIndexParams);
824
825 /*
826 * Select name for index if caller didn't specify
827 */
828 indexRelationName = stmt->idxname;
829 if (indexRelationName == NULL)
830 indexRelationName = ChooseIndexName(RelationGetRelationName(rel),
831 namespaceId,
832 indexColNames,
833 stmt->excludeOpNames,
834 stmt->primary,
835 stmt->isconstraint);
836
837 /*
838 * look up the access method, verify it can handle the requested features
839 */
840 accessMethodName = stmt->accessMethod;
841 tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
842 if (!HeapTupleIsValid(tuple))
843 {
844 /*
845 * Hack to provide more-or-less-transparent updating of old RTREE
846 * indexes to GiST: if RTREE is requested and not found, use GIST.
847 */
848 if (strcmp(accessMethodName, "rtree") == 0)
849 {
851 (errmsg("substituting access method \"gist\" for obsolete method \"rtree\"")));
852 accessMethodName = "gist";
853 tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
854 }
855
856 if (!HeapTupleIsValid(tuple))
858 (errcode(ERRCODE_UNDEFINED_OBJECT),
859 errmsg("access method \"%s\" does not exist",
860 accessMethodName)));
861 }
862 accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
863 accessMethodId = accessMethodForm->oid;
864 amRoutine = GetIndexAmRoutine(accessMethodForm->amhandler);
865
867 accessMethodId);
868
869 if (stmt->unique && !stmt->iswithoutoverlaps && !amRoutine->amcanunique)
871 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
872 errmsg("access method \"%s\" does not support unique indexes",
873 accessMethodName)));
874 if (stmt->indexIncludingParams != NIL && !amRoutine->amcaninclude)
876 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
877 errmsg("access method \"%s\" does not support included columns",
878 accessMethodName)));
879 if (numberOfKeyAttributes > 1 && !amRoutine->amcanmulticol)
881 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
882 errmsg("access method \"%s\" does not support multicolumn indexes",
883 accessMethodName)));
884 if (exclusion && amRoutine->amgettuple == NULL)
886 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
887 errmsg("access method \"%s\" does not support exclusion constraints",
888 accessMethodName)));
889 if (stmt->iswithoutoverlaps && strcmp(accessMethodName, "gist") != 0)
891 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
892 errmsg("access method \"%s\" does not support WITHOUT OVERLAPS constraints",
893 accessMethodName)));
894
895 amcanorder = amRoutine->amcanorder;
896 amoptions = amRoutine->amoptions;
897 amissummarizing = amRoutine->amsummarizing;
898
899 pfree(amRoutine);
900 ReleaseSysCache(tuple);
901
902 /*
903 * Validate predicate, if given
904 */
905 if (stmt->whereClause)
906 CheckPredicate((Expr *) stmt->whereClause);
907
908 /*
909 * Parse AM-specific options, convert to text array form, validate.
910 */
911 reloptions = transformRelOptions((Datum) 0, stmt->options,
912 NULL, NULL, false, false);
913
914 (void) index_reloptions(amoptions, reloptions, true);
915
916 /*
917 * Prepare arguments for index_create, primarily an IndexInfo structure.
918 * Note that predicates must be in implicit-AND format. In a concurrent
919 * build, mark it not-ready-for-inserts.
920 */
921 indexInfo = makeIndexInfo(numberOfAttributes,
922 numberOfKeyAttributes,
923 accessMethodId,
924 NIL, /* expressions, NIL for now */
925 make_ands_implicit((Expr *) stmt->whereClause),
926 stmt->unique,
927 stmt->nulls_not_distinct,
928 !concurrent,
929 concurrent,
930 amissummarizing,
931 stmt->iswithoutoverlaps);
932
933 typeIds = palloc_array(Oid, numberOfAttributes);
934 collationIds = palloc_array(Oid, numberOfAttributes);
935 opclassIds = palloc_array(Oid, numberOfAttributes);
936 opclassOptions = palloc_array(Datum, numberOfAttributes);
937 coloptions = palloc_array(int16, numberOfAttributes);
938 ComputeIndexAttrs(indexInfo,
939 typeIds, collationIds, opclassIds, opclassOptions,
940 coloptions, allIndexParams,
941 stmt->excludeOpNames, tableId,
942 accessMethodName, accessMethodId,
943 amcanorder, stmt->isconstraint, stmt->iswithoutoverlaps,
944 root_save_userid, root_save_sec_context,
945 &root_save_nestlevel);
946
947 /*
948 * Extra checks when creating a PRIMARY KEY index.
949 */
950 if (stmt->primary)
951 index_check_primary_key(rel, indexInfo, is_alter_table, stmt);
952
953 /*
954 * If this table is partitioned and we're creating a unique index, primary
955 * key, or exclusion constraint, make sure that the partition key is a
956 * subset of the index's columns. Otherwise it would be possible to
957 * violate uniqueness by putting values that ought to be unique in
958 * different partitions.
959 *
960 * We could lift this limitation if we had global indexes, but those have
961 * their own problems, so this is a useful feature combination.
962 */
963 if (partitioned && (stmt->unique || exclusion))
964 {
966 const char *constraint_type;
967 int i;
968
969 if (stmt->primary)
970 constraint_type = "PRIMARY KEY";
971 else if (stmt->unique)
972 constraint_type = "UNIQUE";
973 else if (stmt->excludeOpNames)
974 constraint_type = "EXCLUDE";
975 else
976 {
977 elog(ERROR, "unknown constraint type");
978 constraint_type = NULL; /* keep compiler quiet */
979 }
980
981 /*
982 * Verify that all the columns in the partition key appear in the
983 * unique key definition, with the same notion of equality.
984 */
985 for (i = 0; i < key->partnatts; i++)
986 {
987 bool found = false;
988 int eq_strategy;
989 Oid ptkey_eqop;
990 int j;
991
992 /*
993 * Identify the equality operator associated with this partkey
994 * column. For list and range partitioning, partkeys use btree
995 * operator classes; hash partitioning uses hash operator classes.
996 * (Keep this in sync with ComputePartitionAttrs!)
997 */
998 if (key->strategy == PARTITION_STRATEGY_HASH)
999 eq_strategy = HTEqualStrategyNumber;
1000 else
1001 eq_strategy = BTEqualStrategyNumber;
1002
1003 ptkey_eqop = get_opfamily_member(key->partopfamily[i],
1004 key->partopcintype[i],
1005 key->partopcintype[i],
1006 eq_strategy);
1007 if (!OidIsValid(ptkey_eqop))
1008 elog(ERROR, "missing operator %d(%u,%u) in partition opfamily %u",
1009 eq_strategy, key->partopcintype[i], key->partopcintype[i],
1010 key->partopfamily[i]);
1011
1012 /*
1013 * It may be possible to support UNIQUE constraints when partition
1014 * keys are expressions, but is it worth it? Give up for now.
1015 */
1016 if (key->partattrs[i] == 0)
1017 ereport(ERROR,
1018 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1019 errmsg("unsupported %s constraint with partition key definition",
1020 constraint_type),
1021 errdetail("%s constraints cannot be used when partition keys include expressions.",
1022 constraint_type)));
1023
1024 /* Search the index column(s) for a match */
1025 for (j = 0; j < indexInfo->ii_NumIndexKeyAttrs; j++)
1026 {
1027 if (key->partattrs[i] == indexInfo->ii_IndexAttrNumbers[j])
1028 {
1029 /*
1030 * Matched the column, now what about the collation and
1031 * equality op?
1032 */
1033 Oid idx_opfamily;
1034 Oid idx_opcintype;
1035
1036 if (key->partcollation[i] != collationIds[j])
1037 continue;
1038
1039 if (get_opclass_opfamily_and_input_type(opclassIds[j],
1040 &idx_opfamily,
1041 &idx_opcintype))
1042 {
1043 Oid idx_eqop = InvalidOid;
1044
1045 if (stmt->unique && !stmt->iswithoutoverlaps)
1046 idx_eqop = get_opfamily_member_for_cmptype(idx_opfamily,
1047 idx_opcintype,
1048 idx_opcintype,
1049 COMPARE_EQ);
1050 else if (exclusion)
1051 idx_eqop = indexInfo->ii_ExclusionOps[j];
1052
1053 if (!idx_eqop)
1054 ereport(ERROR,
1055 errcode(ERRCODE_UNDEFINED_OBJECT),
1056 errmsg("could not identify an equality operator for type %s", format_type_be(idx_opcintype)),
1057 errdetail("There is no suitable operator in operator family \"%s\" for access method \"%s\".",
1058 get_opfamily_name(idx_opfamily, false), get_am_name(get_opfamily_method(idx_opfamily))));
1059
1060 if (ptkey_eqop == idx_eqop)
1061 {
1062 found = true;
1063 break;
1064 }
1065 else if (exclusion)
1066 {
1067 /*
1068 * We found a match, but it's not an equality
1069 * operator. Instead of failing below with an
1070 * error message about a missing column, fail now
1071 * and explain that the operator is wrong.
1072 */
1073 Form_pg_attribute att = TupleDescAttr(RelationGetDescr(rel), key->partattrs[i] - 1);
1074
1075 ereport(ERROR,
1076 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1077 errmsg("cannot match partition key to index on column \"%s\" using non-equal operator \"%s\"",
1078 NameStr(att->attname),
1079 get_opname(indexInfo->ii_ExclusionOps[j]))));
1080 }
1081 }
1082 }
1083 }
1084
1085 if (!found)
1086 {
1088
1090 key->partattrs[i] - 1);
1091 ereport(ERROR,
1092 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1093 errmsg("unique constraint on partitioned table must include all partitioning columns"),
1094 errdetail("%s constraint on table \"%s\" lacks column \"%s\" which is part of the partition key.",
1095 constraint_type, RelationGetRelationName(rel),
1096 NameStr(att->attname))));
1097 }
1098 }
1099 }
1100
1101
1102 /*
1103 * We disallow indexes on system columns. They would not necessarily get
1104 * updated correctly, and they don't seem useful anyway.
1105 *
1106 * Also disallow virtual generated columns in indexes (use expression
1107 * index instead).
1108 */
1109 for (int i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
1110 {
1111 AttrNumber attno = indexInfo->ii_IndexAttrNumbers[i];
1112
1113 if (attno < 0)
1114 ereport(ERROR,
1115 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1116 errmsg("index creation on system columns is not supported")));
1117
1118
1119 if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
1120 ereport(ERROR,
1121 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1122 stmt->isconstraint ?
1123 errmsg("unique constraints on virtual generated columns are not supported") :
1124 errmsg("indexes on virtual generated columns are not supported")));
1125 }
1126
1127 /*
1128 * Also check for system and generated columns used in expressions or
1129 * predicates.
1130 */
1131 if (indexInfo->ii_Expressions || indexInfo->ii_Predicate)
1132 {
1133 Bitmapset *indexattrs = NULL;
1134 int j;
1135
1136 pull_varattnos((Node *) indexInfo->ii_Expressions, 1, &indexattrs);
1137 pull_varattnos((Node *) indexInfo->ii_Predicate, 1, &indexattrs);
1138
1139 for (int i = FirstLowInvalidHeapAttributeNumber + 1; i < 0; i++)
1140 {
1142 indexattrs))
1143 ereport(ERROR,
1144 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1145 errmsg("index creation on system columns is not supported")));
1146 }
1147
1148 /*
1149 * XXX Virtual generated columns in index expressions or predicates
1150 * could be supported, but it needs support in
1151 * RelationGetIndexExpressions() and RelationGetIndexPredicate().
1152 */
1153 j = -1;
1154 while ((j = bms_next_member(indexattrs, j)) >= 0)
1155 {
1157
1158 if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
1159 ereport(ERROR,
1160 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1161 stmt->isconstraint ?
1162 errmsg("unique constraints on virtual generated columns are not supported") :
1163 errmsg("indexes on virtual generated columns are not supported")));
1164 }
1165 }
1166
1167 /* Is index safe for others to ignore? See set_indexsafe_procflags() */
1168 safe_index = indexInfo->ii_Expressions == NIL &&
1169 indexInfo->ii_Predicate == NIL;
1170
1171 /*
1172 * Report index creation if appropriate (delay this till after most of the
1173 * error checks)
1174 */
1175 if (stmt->isconstraint && !quiet)
1176 {
1177 const char *constraint_type;
1178
1179 if (stmt->primary)
1180 constraint_type = "PRIMARY KEY";
1181 else if (stmt->unique)
1182 constraint_type = "UNIQUE";
1183 else if (stmt->excludeOpNames)
1184 constraint_type = "EXCLUDE";
1185 else
1186 {
1187 elog(ERROR, "unknown constraint type");
1188 constraint_type = NULL; /* keep compiler quiet */
1189 }
1190
1192 (errmsg_internal("%s %s will create implicit index \"%s\" for table \"%s\"",
1193 is_alter_table ? "ALTER TABLE / ADD" : "CREATE TABLE /",
1194 constraint_type,
1195 indexRelationName, RelationGetRelationName(rel))));
1196 }
1197
1198 /*
1199 * A valid stmt->oldNumber implies that we already have a built form of
1200 * the index. The caller should also decline any index build.
1201 */
1202 Assert(!RelFileNumberIsValid(stmt->oldNumber) || (skip_build && !concurrent));
1203
1204 /*
1205 * Make the catalog entries for the index, including constraints. This
1206 * step also actually builds the index, except if caller requested not to
1207 * or in concurrent mode, in which case it'll be done later, or doing a
1208 * partitioned index (because those don't have storage).
1209 */
1210 flags = constr_flags = 0;
1211 if (stmt->isconstraint)
1213 if (skip_build || concurrent || partitioned)
1214 flags |= INDEX_CREATE_SKIP_BUILD;
1215 if (stmt->if_not_exists)
1217 if (concurrent)
1218 flags |= INDEX_CREATE_CONCURRENT;
1219 if (partitioned)
1220 flags |= INDEX_CREATE_PARTITIONED;
1221 if (stmt->primary)
1222 flags |= INDEX_CREATE_IS_PRIMARY;
1223
1224 /*
1225 * If the table is partitioned, and recursion was declined but partitions
1226 * exist, mark the index as invalid.
1227 */
1228 if (partitioned && stmt->relation && !stmt->relation->inh)
1229 {
1231
1232 if (pd->nparts != 0)
1233 flags |= INDEX_CREATE_INVALID;
1234 }
1235
1236 if (stmt->deferrable)
1237 constr_flags |= INDEX_CONSTR_CREATE_DEFERRABLE;
1238 if (stmt->initdeferred)
1239 constr_flags |= INDEX_CONSTR_CREATE_INIT_DEFERRED;
1240 if (stmt->iswithoutoverlaps)
1242
1243 indexRelationId =
1244 index_create(rel, indexRelationName, indexRelationId, parentIndexId,
1245 parentConstraintId,
1246 stmt->oldNumber, indexInfo, indexColNames,
1247 accessMethodId, tablespaceId,
1248 collationIds, opclassIds, opclassOptions,
1249 coloptions, NULL, reloptions,
1250 flags, constr_flags,
1251 allowSystemTableMods, !check_rights,
1252 &createdConstraintId);
1253
1254 ObjectAddressSet(address, RelationRelationId, indexRelationId);
1255
1256 if (!OidIsValid(indexRelationId))
1257 {
1258 /*
1259 * Roll back any GUC changes executed by index functions. Also revert
1260 * to original default_tablespace if we changed it above.
1261 */
1262 AtEOXact_GUC(false, root_save_nestlevel);
1263
1264 /* Restore userid and security context */
1265 SetUserIdAndSecContext(root_save_userid, root_save_sec_context);
1266
1267 table_close(rel, NoLock);
1268
1269 /* If this is the top-level index, we're done */
1270 if (!OidIsValid(parentIndexId))
1272
1273 return address;
1274 }
1275
1276 /*
1277 * Roll back any GUC changes executed by index functions, and keep
1278 * subsequent changes local to this command. This is essential if some
1279 * index function changed a behavior-affecting GUC, e.g. search_path.
1280 */
1281 AtEOXact_GUC(false, root_save_nestlevel);
1282 root_save_nestlevel = NewGUCNestLevel();
1284
1285 /* Add any requested comment */
1286 if (stmt->idxcomment != NULL)
1287 CreateComments(indexRelationId, RelationRelationId, 0,
1288 stmt->idxcomment);
1289
1290 if (partitioned)
1291 {
1292 PartitionDesc partdesc;
1293
1294 /*
1295 * Unless caller specified to skip this step (via ONLY), process each
1296 * partition to make sure they all contain a corresponding index.
1297 *
1298 * If we're called internally (no stmt->relation), recurse always.
1299 */
1300 partdesc = RelationGetPartitionDesc(rel, true);
1301 if ((!stmt->relation || stmt->relation->inh) && partdesc->nparts > 0)
1302 {
1303 int nparts = partdesc->nparts;
1304 Oid *part_oids = palloc_array(Oid, nparts);
1305 bool invalidate_parent = false;
1306 Relation parentIndex;
1307 TupleDesc parentDesc;
1308
1309 /*
1310 * Report the total number of partitions at the start of the
1311 * command; don't update it when being called recursively.
1312 */
1313 if (!OidIsValid(parentIndexId))
1314 {
1315 /*
1316 * When called by ProcessUtilitySlow, the number of partitions
1317 * is passed in as an optimization; but other callers pass -1
1318 * since they don't have the value handy. This should count
1319 * partitions the same way, ie one less than the number of
1320 * relations find_all_inheritors reports.
1321 *
1322 * We assume we needn't ask find_all_inheritors to take locks,
1323 * because that should have happened already for all callers.
1324 * Even if it did not, this is safe as long as we don't try to
1325 * touch the partitions here; the worst consequence would be a
1326 * bogus progress-reporting total.
1327 */
1328 if (total_parts < 0)
1329 {
1330 List *children = find_all_inheritors(tableId, NoLock, NULL);
1331
1332 total_parts = list_length(children) - 1;
1333 list_free(children);
1334 }
1335
1337 total_parts);
1338 }
1339
1340 /* Make a local copy of partdesc->oids[], just for safety */
1341 memcpy(part_oids, partdesc->oids, sizeof(Oid) * nparts);
1342
1343 /*
1344 * We'll need an IndexInfo describing the parent index. The one
1345 * built above is almost good enough, but not quite, because (for
1346 * example) its predicate expression if any hasn't been through
1347 * expression preprocessing. The most reliable way to get an
1348 * IndexInfo that will match those for child indexes is to build
1349 * it the same way, using BuildIndexInfo().
1350 */
1351 parentIndex = index_open(indexRelationId, lockmode);
1352 indexInfo = BuildIndexInfo(parentIndex);
1353
1354 parentDesc = RelationGetDescr(rel);
1355
1356 /*
1357 * For each partition, scan all existing indexes; if one matches
1358 * our index definition and is not already attached to some other
1359 * parent index, attach it to the one we just created.
1360 *
1361 * If none matches, build a new index by calling ourselves
1362 * recursively with the same options (except for the index name).
1363 */
1364 for (int i = 0; i < nparts; i++)
1365 {
1366 Oid childRelid = part_oids[i];
1367 Relation childrel;
1368 Oid child_save_userid;
1369 int child_save_sec_context;
1370 int child_save_nestlevel;
1371 List *childidxs;
1372 ListCell *cell;
1373 AttrMap *attmap;
1374 bool found = false;
1375
1376 childrel = table_open(childRelid, lockmode);
1377
1378 GetUserIdAndSecContext(&child_save_userid,
1379 &child_save_sec_context);
1380 SetUserIdAndSecContext(childrel->rd_rel->relowner,
1381 child_save_sec_context | SECURITY_RESTRICTED_OPERATION);
1382 child_save_nestlevel = NewGUCNestLevel();
1384
1385 /*
1386 * Don't try to create indexes on foreign tables, though. Skip
1387 * those if a regular index, or fail if trying to create a
1388 * constraint index.
1389 */
1390 if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1391 {
1392 if (stmt->unique || stmt->primary)
1393 ereport(ERROR,
1394 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1395 errmsg("cannot create unique index on partitioned table \"%s\"",
1397 errdetail("Table \"%s\" contains partitions that are foreign tables.",
1399
1400 AtEOXact_GUC(false, child_save_nestlevel);
1401 SetUserIdAndSecContext(child_save_userid,
1402 child_save_sec_context);
1403 table_close(childrel, lockmode);
1404 continue;
1405 }
1406
1407 childidxs = RelationGetIndexList(childrel);
1408 attmap =
1410 parentDesc,
1411 false);
1412
1413 foreach(cell, childidxs)
1414 {
1415 Oid cldidxid = lfirst_oid(cell);
1416 Relation cldidx;
1417 IndexInfo *cldIdxInfo;
1418
1419 /* this index is already partition of another one */
1420 if (has_superclass(cldidxid))
1421 continue;
1422
1423 cldidx = index_open(cldidxid, lockmode);
1424 cldIdxInfo = BuildIndexInfo(cldidx);
1425 if (CompareIndexInfo(cldIdxInfo, indexInfo,
1426 cldidx->rd_indcollation,
1427 parentIndex->rd_indcollation,
1428 cldidx->rd_opfamily,
1429 parentIndex->rd_opfamily,
1430 attmap))
1431 {
1432 Oid cldConstrOid = InvalidOid;
1433
1434 /*
1435 * Found a match.
1436 *
1437 * If this index is being created in the parent
1438 * because of a constraint, then the child needs to
1439 * have a constraint also, so look for one. If there
1440 * is no such constraint, this index is no good, so
1441 * keep looking.
1442 */
1443 if (createdConstraintId != InvalidOid)
1444 {
1445 cldConstrOid =
1447 cldidxid);
1448 if (cldConstrOid == InvalidOid)
1449 {
1450 index_close(cldidx, lockmode);
1451 continue;
1452 }
1453 }
1454
1455 /* Attach index to parent and we're done. */
1456 IndexSetParentIndex(cldidx, indexRelationId);
1457 if (createdConstraintId != InvalidOid)
1458 ConstraintSetParentConstraint(cldConstrOid,
1459 createdConstraintId,
1460 childRelid);
1461
1462 if (!cldidx->rd_index->indisvalid)
1463 invalidate_parent = true;
1464
1465 found = true;
1466
1467 /*
1468 * Report this partition as processed. Note that if
1469 * the partition has children itself, we'd ideally
1470 * count the children and update the progress report
1471 * for all of them; but that seems unduly expensive.
1472 * Instead, the progress report will act like all such
1473 * indirect children were processed in zero time at
1474 * the end of the command.
1475 */
1477
1478 /* keep lock till commit */
1479 index_close(cldidx, NoLock);
1480 break;
1481 }
1482
1483 index_close(cldidx, lockmode);
1484 }
1485
1486 list_free(childidxs);
1487 AtEOXact_GUC(false, child_save_nestlevel);
1488 SetUserIdAndSecContext(child_save_userid,
1489 child_save_sec_context);
1490 table_close(childrel, NoLock);
1491
1492 /*
1493 * If no matching index was found, create our own.
1494 */
1495 if (!found)
1496 {
1497 IndexStmt *childStmt;
1498 ObjectAddress childAddr;
1499
1500 /*
1501 * Build an IndexStmt describing the desired child index
1502 * in the same way that we do during ATTACH PARTITION.
1503 * Notably, we rely on generateClonedIndexStmt to produce
1504 * a search-path-independent representation, which the
1505 * original IndexStmt might not be.
1506 */
1507 childStmt = generateClonedIndexStmt(NULL,
1508 parentIndex,
1509 attmap,
1510 NULL);
1511
1512 /*
1513 * Recurse as the starting user ID. Callee will use that
1514 * for permission checks, then switch again.
1515 */
1516 Assert(GetUserId() == child_save_userid);
1517 SetUserIdAndSecContext(root_save_userid,
1518 root_save_sec_context);
1519 childAddr =
1520 DefineIndex(childRelid, childStmt,
1521 InvalidOid, /* no predefined OID */
1522 indexRelationId, /* this is our child */
1523 createdConstraintId,
1524 -1,
1525 is_alter_table, check_rights,
1526 check_not_in_use,
1527 skip_build, quiet);
1528 SetUserIdAndSecContext(child_save_userid,
1529 child_save_sec_context);
1530
1531 /*
1532 * Check if the index just created is valid or not, as it
1533 * could be possible that it has been switched as invalid
1534 * when recursing across multiple partition levels.
1535 */
1536 if (!get_index_isvalid(childAddr.objectId))
1537 invalidate_parent = true;
1538 }
1539
1540 free_attrmap(attmap);
1541 }
1542
1543 index_close(parentIndex, lockmode);
1544
1545 /*
1546 * The pg_index row we inserted for this index was marked
1547 * indisvalid=true. But if we attached an existing index that is
1548 * invalid, this is incorrect, so update our row to invalid too.
1549 */
1550 if (invalidate_parent)
1551 {
1552 Relation pg_index = table_open(IndexRelationId, RowExclusiveLock);
1553 HeapTuple tup,
1554 newtup;
1555
1556 tup = SearchSysCache1(INDEXRELID,
1557 ObjectIdGetDatum(indexRelationId));
1558 if (!HeapTupleIsValid(tup))
1559 elog(ERROR, "cache lookup failed for index %u",
1560 indexRelationId);
1561 newtup = heap_copytuple(tup);
1562 ((Form_pg_index) GETSTRUCT(newtup))->indisvalid = false;
1563 CatalogTupleUpdate(pg_index, &tup->t_self, newtup);
1564 ReleaseSysCache(tup);
1565 table_close(pg_index, RowExclusiveLock);
1566 heap_freetuple(newtup);
1567
1568 /*
1569 * CCI here to make this update visible, in case this recurses
1570 * across multiple partition levels.
1571 */
1573 }
1574 }
1575
1576 /*
1577 * Indexes on partitioned tables are not themselves built, so we're
1578 * done here.
1579 */
1580 AtEOXact_GUC(false, root_save_nestlevel);
1581 SetUserIdAndSecContext(root_save_userid, root_save_sec_context);
1582 table_close(rel, NoLock);
1583 if (!OidIsValid(parentIndexId))
1585 else
1586 {
1587 /* Update progress for an intermediate partitioned index itself */
1589 }
1590
1591 return address;
1592 }
1593
1594 AtEOXact_GUC(false, root_save_nestlevel);
1595 SetUserIdAndSecContext(root_save_userid, root_save_sec_context);
1596
1597 if (!concurrent)
1598 {
1599 /* Close the heap and we're done, in the non-concurrent case */
1600 table_close(rel, NoLock);
1601
1602 /*
1603 * If this is the top-level index, the command is done overall;
1604 * otherwise, increment progress to report one child index is done.
1605 */
1606 if (!OidIsValid(parentIndexId))
1608 else
1610
1611 return address;
1612 }
1613
1614 /* save lockrelid and locktag for below, then close rel */
1615 heaprelid = rel->rd_lockInfo.lockRelId;
1616 SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId);
1617 table_close(rel, NoLock);
1618
1619 /*
1620 * For a concurrent build, it's important to make the catalog entries
1621 * visible to other transactions before we start to build the index. That
1622 * will prevent them from making incompatible HOT updates. The new index
1623 * will be marked not indisready and not indisvalid, so that no one else
1624 * tries to either insert into it or use it for queries.
1625 *
1626 * We must commit our current transaction so that the index becomes
1627 * visible; then start another. Note that all the data structures we just
1628 * built are lost in the commit. The only data we keep past here are the
1629 * relation IDs.
1630 *
1631 * Before committing, get a session-level lock on the table, to ensure
1632 * that neither it nor the index can be dropped before we finish. This
1633 * cannot block, even if someone else is waiting for access, because we
1634 * already have the same lock within our transaction.
1635 *
1636 * Note: we don't currently bother with a session lock on the index,
1637 * because there are no operations that could change its state while we
1638 * hold lock on the parent table. This might need to change later.
1639 */
1641
1645
1646 /* Tell concurrent index builds to ignore us, if index qualifies */
1647 if (safe_index)
1649
1650 /*
1651 * The index is now visible, so we can report the OID. While on it,
1652 * include the report for the beginning of phase 2.
1653 */
1654 {
1655 const int progress_cols[] = {
1658 };
1659 const int64 progress_vals[] = {
1660 indexRelationId,
1662 };
1663
1664 pgstat_progress_update_multi_param(2, progress_cols, progress_vals);
1665 }
1666
1667 /*
1668 * Phase 2 of concurrent index build (see comments for validate_index()
1669 * for an overview of how this works)
1670 *
1671 * Now we must wait until no running transaction could have the table open
1672 * with the old list of indexes. Use ShareLock to consider running
1673 * transactions that hold locks that permit writing to the table. Note we
1674 * do not need to worry about xacts that open the table for writing after
1675 * this point; they will see the new index when they open it.
1676 *
1677 * Note: the reason we use actual lock acquisition here, rather than just
1678 * checking the ProcArray and sleeping, is that deadlock is possible if
1679 * one of the transactions in question is blocked trying to acquire an
1680 * exclusive lock on our table. The lock code will detect deadlock and
1681 * error out properly.
1682 */
1683 WaitForLockers(heaplocktag, ShareLock, true);
1684
1685 /*
1686 * At this moment we are sure that there are no transactions with the
1687 * table open for write that don't have this new index in their list of
1688 * indexes. We have waited out all the existing transactions and any new
1689 * transaction will have the new index in its list, but the index is still
1690 * marked as "not-ready-for-inserts". The index is consulted while
1691 * deciding HOT-safety though. This arrangement ensures that no new HOT
1692 * chains can be created where the new tuple and the old tuple in the
1693 * chain have different index keys.
1694 *
1695 * We now take a new snapshot, and build the index using all tuples that
1696 * are visible in this snapshot. We can be sure that any HOT updates to
1697 * these tuples will be compatible with the index, since any updates made
1698 * by transactions that didn't know about the index are now committed or
1699 * rolled back. Thus, each visible tuple is either the end of its
1700 * HOT-chain or the extension of the chain is HOT-safe for this index.
1701 */
1702
1703 /* Set ActiveSnapshot since functions in the indexes may need it */
1705
1706 /* Perform concurrent build of index */
1707 index_concurrently_build(tableId, indexRelationId);
1708
1709 /* we can do away with our snapshot */
1711
1712 /*
1713 * Commit this transaction to make the indisready update visible.
1714 */
1717
1718 /* Tell concurrent index builds to ignore us, if index qualifies */
1719 if (safe_index)
1721
1722 /*
1723 * Phase 3 of concurrent index build
1724 *
1725 * We once again wait until no transaction can have the table open with
1726 * the index marked as read-only for updates.
1727 */
1730 WaitForLockers(heaplocktag, ShareLock, true);
1731
1732 /*
1733 * Now take the "reference snapshot" that will be used by validate_index()
1734 * to filter candidate tuples. Beware! There might still be snapshots in
1735 * use that treat some transaction as in-progress that our reference
1736 * snapshot treats as committed. If such a recently-committed transaction
1737 * deleted tuples in the table, we will not include them in the index; yet
1738 * those transactions which see the deleting one as still-in-progress will
1739 * expect such tuples to be there once we mark the index as valid.
1740 *
1741 * We solve this by waiting for all endangered transactions to exit before
1742 * we mark the index as valid.
1743 *
1744 * We also set ActiveSnapshot to this snap, since functions in indexes may
1745 * need a snapshot.
1746 */
1748 PushActiveSnapshot(snapshot);
1749
1750 /*
1751 * Scan the index and the heap, insert any missing index entries.
1752 */
1753 validate_index(tableId, indexRelationId, snapshot);
1754
1755 /*
1756 * Drop the reference snapshot. We must do this before waiting out other
1757 * snapshot holders, else we will deadlock against other processes also
1758 * doing CREATE INDEX CONCURRENTLY, which would see our snapshot as one
1759 * they must wait for. But first, save the snapshot's xmin to use as
1760 * limitXmin for GetCurrentVirtualXIDs().
1761 */
1762 limitXmin = snapshot->xmin;
1763
1765 UnregisterSnapshot(snapshot);
1766
1767 /*
1768 * The snapshot subsystem could still contain registered snapshots that
1769 * are holding back our process's advertised xmin; in particular, if
1770 * default_transaction_isolation = serializable, there is a transaction
1771 * snapshot that is still active. The CatalogSnapshot is likewise a
1772 * hazard. To ensure no deadlocks, we must commit and start yet another
1773 * transaction, and do our wait before any snapshot has been taken in it.
1774 */
1777
1778 /* Tell concurrent index builds to ignore us, if index qualifies */
1779 if (safe_index)
1781
1782 /* We should now definitely not be advertising any xmin. */
1784
1785 /*
1786 * The index is now valid in the sense that it contains all currently
1787 * interesting tuples. But since it might not contain tuples deleted just
1788 * before the reference snap was taken, we have to wait out any
1789 * transactions that might have older snapshots.
1790 */
1793 WaitForOlderSnapshots(limitXmin, true);
1794
1795 /*
1796 * Updating pg_index might involve TOAST table access, so ensure we have a
1797 * valid snapshot.
1798 */
1800
1801 /*
1802 * Index can now be marked valid -- update its pg_index entry
1803 */
1805
1807
1808 /*
1809 * The pg_index update will cause backends (including this one) to update
1810 * relcache entries for the index itself, but we should also send a
1811 * relcache inval on the parent table to force replanning of cached plans.
1812 * Otherwise existing sessions might fail to use the new index where it
1813 * would be useful. (Note that our earlier commits did not create reasons
1814 * to replan; so relcache flush on the index itself was sufficient.)
1815 */
1817
1818 /*
1819 * Last thing to do is release the session-level lock on the parent table.
1820 */
1822
1824
1825 return address;
1826}
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2622
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3804
bytea *(* amoptions_function)(Datum reloptions, bool validate)
Definition: amapi.h:163
char * get_am_name(Oid amOid)
Definition: amcmds.c:192
void free_attrmap(AttrMap *map)
Definition: attmap.c:56
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:175
int16 AttrNumber
Definition: attnum.h:21
char * get_tablespace_name(Oid spc_oid)
Definition: tablespace.c:1472
Oid get_tablespace_oid(const char *tablespacename, bool missing_ok)
Definition: tablespace.c:1426
Oid GetDefaultTablespace(char relpersistence, bool partitioned)
Definition: tablespace.c:1143
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
void pgstat_progress_incr_param(int index, int64 incr)
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
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
#define NameStr(name)
Definition: c.h:717
uint16 bits16
Definition: c.h:510
int64_t int64
Definition: c.h:499
uint32 TransactionId
Definition: c.h:623
void CreateComments(Oid oid, Oid classoid, int32 subid, const char *comment)
Definition: comment.c:143
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1157
#define DEBUG1
Definition: elog.h:30
#define NOTICE
Definition: elog.h:35
bool allowSystemTableMods
Definition: globals.c:129
Oid MyDatabaseTableSpace
Definition: globals.c:95
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:3342
@ GUC_ACTION_SAVE
Definition: guc.h:205
@ PGC_S_SESSION
Definition: guc.h:126
@ PGC_USERSET
Definition: guc.h:79
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:778
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
#define stmt
Definition: indent_codes.h:59
void validate_index(Oid heapId, Oid indexId, Snapshot snapshot)
Definition: index.c:3350
Oid index_create(Relation heapRelation, const char *indexRelationName, Oid indexRelationId, Oid parentIndexRelid, Oid parentConstraintId, RelFileNumber relFileNumber, IndexInfo *indexInfo, const List *indexColNames, Oid accessMethodId, Oid tableSpaceId, const Oid *collationIds, const Oid *opclassIds, const Datum *opclassOptions, const int16 *coloptions, const NullableDatum *stattargets, Datum reloptions, bits16 flags, bits16 constr_flags, bool allow_system_table_mods, bool is_internal, Oid *constraintId)
Definition: index.c:726
void index_set_state_flags(Oid indexId, IndexStateFlagsAction action)
Definition: index.c:3503
bool CompareIndexInfo(const IndexInfo *info1, const IndexInfo *info2, const Oid *collations1, const Oid *collations2, const Oid *opfamilies1, const Oid *opfamilies2, const AttrMap *attmap)
Definition: index.c:2537
IndexInfo * BuildIndexInfo(Relation index)
Definition: index.c:2428
void index_check_primary_key(Relation heapRel, const IndexInfo *indexInfo, bool is_alter_table, const IndexStmt *stmt)
Definition: index.c:202
void index_concurrently_build(Oid heapRelationId, Oid indexRelationId)
Definition: index.c:1485
#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_CONSTR_CREATE_WITHOUT_OVERLAPS
Definition: index.h:96
#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:92
@ INDEX_CREATE_SET_VALID
Definition: index.h:27
#define INDEX_CONSTR_CREATE_INIT_DEFERRED
Definition: index.h:93
#define INDEX_CREATE_CONCURRENT
Definition: index.h:64
ObjectAddress DefineIndex(Oid tableId, IndexStmt *stmt, Oid indexRelationId, Oid parentIndexId, Oid parentConstraintId, int total_parts, bool is_alter_table, bool check_rights, bool check_not_in_use, bool skip_build, bool quiet)
Definition: indexcmds.c:542
static void set_indexsafe_procflags(void)
Definition: indexcmds.c:4577
void IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
Definition: indexcmds.c:4407
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition: indexcmds.c:435
static void CheckPredicate(Expr *predicate)
Definition: indexcmds.c:1841
static List * ChooseIndexColumnNames(const List *indexElems)
Definition: indexcmds.c:2727
static char * ChooseIndexName(const char *tabname, Oid namespaceId, const List *colnames, const List *exclusionOpNames, bool primary, bool isconstraint)
Definition: indexcmds.c:2638
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition: inval.c:1677
int j
Definition: isn.c:75
List * list_concat_copy(const List *list1, const List *list2)
Definition: list.c:598
void list_free(List *list)
Definition: list.c:1546
void LockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
Definition: lmgr.c:391
void WaitForLockers(LOCKTAG heaplocktag, LOCKMODE lockmode, bool progress)
Definition: lmgr.c:983
void UnlockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
Definition: lmgr.c:404
#define SET_LOCKTAG_RELATION(locktag, dboid, reloid)
Definition: lock.h:182
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:2161
bool get_index_isvalid(Oid index_oid)
Definition: lsyscache.c:3661
Oid get_opfamily_member_for_cmptype(Oid opfamily, Oid lefttype, Oid righttype, CompareType cmptype)
Definition: lsyscache.c:196
bool get_opclass_opfamily_and_input_type(Oid opclass, Oid *opfamily, Oid *opcintype)
Definition: lsyscache.c:1270
char * get_opname(Oid opno)
Definition: lsyscache.c:1393
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:167
Oid get_opfamily_method(Oid opfid)
Definition: lsyscache.c:1319
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3449
List * make_ands_implicit(Expr *clause)
Definition: makefuncs.c:810
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:476
#define SECURITY_RESTRICTED_OPERATION
Definition: miscadmin.h:318
Oid GetUserId(void)
Definition: miscinit.c:520
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
IndexStmt * generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, const AttrMap *attmap, Oid *constraintOid)
@ PARTITION_STRATEGY_HASH
Definition: parsenodes.h:885
@ OBJECT_SCHEMA
Definition: parsenodes.h:2348
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2354
#define ACL_CREATE
Definition: parsenodes.h:85
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:51
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition: partdesc.c:71
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)
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:255
bool has_superclass(Oid relationId)
Definition: pg_inherits.c:377
#define lfirst_oid(lc)
Definition: pg_list.h:174
#define PROGRESS_CREATEIDX_PARTITIONS_DONE
Definition: progress.h:92
#define PROGRESS_CREATEIDX_PHASE_WAIT_1
Definition: progress.h:96
#define PROGRESS_CREATEIDX_COMMAND_CREATE_CONCURRENTLY
Definition: progress.h:114
#define PROGRESS_CREATEIDX_ACCESS_METHOD_OID
Definition: progress.h:86
#define PROGRESS_CREATEIDX_PHASE_WAIT_3
Definition: progress.h:102
#define PROGRESS_CREATEIDX_COMMAND_CREATE
Definition: progress.h:113
#define PROGRESS_CREATEIDX_PHASE_WAIT_2
Definition: progress.h:98
#define PROGRESS_CREATEIDX_PHASE
Definition: progress.h:87
#define PROGRESS_CREATEIDX_INDEX_OID
Definition: progress.h:85
#define PROGRESS_CREATEIDX_PARTITIONS_TOTAL
Definition: progress.h:91
#define PROGRESS_CREATEIDX_COMMAND
Definition: progress.h:84
#define RelationGetDescr(relation)
Definition: rel.h:539
#define RelationGetRelationName(relation)
Definition: rel.h:547
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:666
#define RelationGetNamespace(relation)
Definition: rel.h:554
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4764
bytea * index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
Definition: reloptions.c:2089
#define RelFileNumberIsValid(relnumber)
Definition: relpath.h:27
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:271
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:853
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:669
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:811
void PopActiveSnapshot(void)
Definition: snapmgr.c:762
PGPROC * MyProc
Definition: proc.c:66
#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:301
amgettuple_function amgettuple
Definition: amapi.h:308
bool amcanunique
Definition: amapi.h:255
bool amcanmulticol
Definition: amapi.h:257
bool amcaninclude
Definition: amapi.h:275
int ii_NumIndexAttrs
Definition: execnodes.h:196
List * ii_Predicate
Definition: execnodes.h:201
Definition: lock.h:166
LockRelId lockRelId
Definition: rel.h:46
Definition: rel.h:39
Oid relId
Definition: rel.h:40
Oid dbId
Definition: rel.h:41
TransactionId xmin
Definition: proc.h:178
LockInfoData rd_lockInfo
Definition: rel.h:114
Form_pg_index rd_index
Definition: rel.h:192
Oid * rd_opfamily
Definition: rel.h:207
Oid * rd_indcollation
Definition: rel.h:217
Form_pg_class rd_rel
Definition: rel.h:111
TransactionId xmin
Definition: snapshot.h:153
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:4377
#define InvalidTransactionId
Definition: transam.h:31
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:296
void CommandCounterIncrement(void)
Definition: xact.c:1100
void StartTransactionCommand(void)
Definition: xact.c:3059
void CommitTransactionCommand(void)
Definition: xact.c:3157

References ACL_CREATE, aclcheck_error(), ACLCHECK_OK, allowSystemTableMods, IndexAmRoutine::amcaninclude, IndexAmRoutine::amcanmulticol, IndexAmRoutine::amcanorder, IndexAmRoutine::amcanunique, IndexAmRoutine::amgettuple, IndexAmRoutine::amoptions, IndexAmRoutine::amsummarizing, Assert(), AtEOXact_GUC(), bms_is_member(), bms_next_member(), BTEqualStrategyNumber, build_attrmap_by_name(), BuildIndexInfo(), CacheInvalidateRelcacheByRelid(), CatalogTupleUpdate(), CheckPredicate(), CheckTableNotInUse(), ChooseIndexColumnNames(), ChooseIndexName(), CommandCounterIncrement(), CommitTransactionCommand(), COMPARE_EQ, CompareIndexInfo(), ComputeIndexAttrs(), ConstraintSetParentConstraint(), CreateComments(), LockRelId::dbId, DEBUG1, DefineIndex(), elog, ereport, errcode(), errdetail(), errdetail_relkind_not_supported(), errmsg(), errmsg_internal(), ERROR, find_all_inheritors(), FirstLowInvalidHeapAttributeNumber, format_type_be(), free_attrmap(), generateClonedIndexStmt(), get_am_name(), get_index_isvalid(), get_namespace_name(), get_opclass_opfamily_and_input_type(), get_opfamily_member(), get_opfamily_member_for_cmptype(), get_opfamily_method(), get_opfamily_name(), get_opname(), 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, IndexInfo::ii_ExclusionOps, 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_CONSTR_CREATE_WITHOUT_OVERLAPS, 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(), IndexSetParentIndex(), InvalidOid, InvalidTransactionId, IsBootstrapProcessingMode, j, sort-test::key, lfirst_oid, list_concat_copy(), list_free(), list_length(), LockRelationIdForSession(), LockInfoData::lockRelId, make_ands_implicit(), makeIndexInfo(), MyDatabaseTableSpace, MyProc, NameStr, NewGUCNestLevel(), NIL, NoLock, NOTICE, PartitionDescData::nparts, object_aclcheck(), OBJECT_SCHEMA, OBJECT_TABLESPACE, ObjectAddressSet, ObjectAddress::objectId, ObjectIdGetDatum(), OidIsValid, PartitionDescData::oids, palloc_array, PARTITION_STRATEGY_HASH, pfree(), PGC_S_SESSION, PGC_USERSET, pgstat_progress_end_command(), pgstat_progress_incr_param(), pgstat_progress_start_command(), pgstat_progress_update_multi_param(), pgstat_progress_update_param(), PointerGetDatum(), PopActiveSnapshot(), 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(), RELATION_IS_OTHER_TEMP, RelationGetDescr, RelationGetIndexList(), RelationGetNamespace, RelationGetPartitionDesc(), RelationGetPartitionKey(), RelationGetRelationName, ReleaseSysCache(), RelFileNumberIsValid, LockRelId::relId, RestrictSearchPath(), RowExclusiveLock, SearchSysCache1(), SECURITY_RESTRICTED_OPERATION, set_config_option(), set_indexsafe_procflags(), SET_LOCKTAG_RELATION, SetUserIdAndSecContext(), ShareLock, ShareUpdateExclusiveLock, StartTransactionCommand(), stmt, HeapTupleData::t_self, table_close(), table_open(), transformRelOptions(), TupleDescAttr(), UnlockRelationIdForSession(), UnregisterSnapshot(), validate_index(), WaitForLockers(), WaitForOlderSnapshots(), PGPROC::xmin, and SnapshotData::xmin.

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

◆ ExecReindex()

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

Definition at line 2788 of file indexcmds.c.

2789{
2790 ReindexParams params = {0};
2791 ListCell *lc;
2792 bool concurrently = false;
2793 bool verbose = false;
2794 char *tablespacename = NULL;
2795
2796 /* Parse option list */
2797 foreach(lc, stmt->params)
2798 {
2799 DefElem *opt = (DefElem *) lfirst(lc);
2800
2801 if (strcmp(opt->defname, "verbose") == 0)
2802 verbose = defGetBoolean(opt);
2803 else if (strcmp(opt->defname, "concurrently") == 0)
2804 concurrently = defGetBoolean(opt);
2805 else if (strcmp(opt->defname, "tablespace") == 0)
2806 tablespacename = defGetString(opt);
2807 else
2808 ereport(ERROR,
2809 (errcode(ERRCODE_SYNTAX_ERROR),
2810 errmsg("unrecognized REINDEX option \"%s\"",
2811 opt->defname),
2812 parser_errposition(pstate, opt->location)));
2813 }
2814
2815 if (concurrently)
2816 PreventInTransactionBlock(isTopLevel,
2817 "REINDEX CONCURRENTLY");
2818
2819 params.options =
2820 (verbose ? REINDEXOPT_VERBOSE : 0) |
2821 (concurrently ? REINDEXOPT_CONCURRENTLY : 0);
2822
2823 /*
2824 * Assign the tablespace OID to move indexes to, with InvalidOid to do
2825 * nothing.
2826 */
2827 if (tablespacename != NULL)
2828 {
2829 params.tablespaceOid = get_tablespace_oid(tablespacename, false);
2830
2831 /* Check permissions except when moving to database's default */
2832 if (OidIsValid(params.tablespaceOid) &&
2834 {
2835 AclResult aclresult;
2836
2837 aclresult = object_aclcheck(TableSpaceRelationId, params.tablespaceOid,
2839 if (aclresult != ACLCHECK_OK)
2842 }
2843 }
2844 else
2845 params.tablespaceOid = InvalidOid;
2846
2847 switch (stmt->kind)
2848 {
2850 ReindexIndex(stmt, &params, isTopLevel);
2851 break;
2853 ReindexTable(stmt, &params, isTopLevel);
2854 break;
2858
2859 /*
2860 * This cannot run inside a user transaction block; if we were
2861 * inside a transaction, then its commit- and
2862 * start-transaction-command calls would not have the intended
2863 * effect!
2864 */
2865 PreventInTransactionBlock(isTopLevel,
2866 (stmt->kind == REINDEX_OBJECT_SCHEMA) ? "REINDEX SCHEMA" :
2867 (stmt->kind == REINDEX_OBJECT_SYSTEM) ? "REINDEX SYSTEM" :
2868 "REINDEX DATABASE");
2869 ReindexMultipleTables(stmt, &params);
2870 break;
2871 default:
2872 elog(ERROR, "unrecognized object type: %d",
2873 (int) stmt->kind);
2874 break;
2875 }
2876}
char * defGetString(DefElem *def)
Definition: define.c:35
bool defGetBoolean(DefElem *def)
Definition: define.c:94
int verbose
#define REINDEXOPT_CONCURRENTLY
Definition: index.h:44
#define REINDEXOPT_VERBOSE
Definition: index.h:41
static void ReindexIndex(const ReindexStmt *stmt, const ReindexParams *params, bool isTopLevel)
Definition: indexcmds.c:2883
static void ReindexMultipleTables(const ReindexStmt *stmt, const ReindexParams *params)
Definition: indexcmds.c:3072
static Oid ReindexTable(const ReindexStmt *stmt, const ReindexParams *params, bool isTopLevel)
Definition: indexcmds.c:3013
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
@ REINDEX_OBJECT_DATABASE
Definition: parsenodes.h:4060
@ REINDEX_OBJECT_INDEX
Definition: parsenodes.h:4056
@ REINDEX_OBJECT_SCHEMA
Definition: parsenodes.h:4058
@ REINDEX_OBJECT_SYSTEM
Definition: parsenodes.h:4059
@ REINDEX_OBJECT_TABLE
Definition: parsenodes.h:4057
char * defname
Definition: parsenodes.h:826
ParseLoc location
Definition: parsenodes.h:830
Oid tablespaceOid
Definition: index.h:36
bits32 options
Definition: index.h:35
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition: xact.c:3648

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

Referenced by ProcessUtilitySlow().

◆ GetDefaultOpClass()

Oid GetDefaultOpClass ( Oid  type_id,
Oid  am_id 
)

Definition at line 2343 of file indexcmds.c.

2344{
2345 Oid result = InvalidOid;
2346 int nexact = 0;
2347 int ncompatible = 0;
2348 int ncompatiblepreferred = 0;
2349 Relation rel;
2350 ScanKeyData skey[1];
2351 SysScanDesc scan;
2352 HeapTuple tup;
2353 TYPCATEGORY tcategory;
2354
2355 /* If it's a domain, look at the base type instead */
2356 type_id = getBaseType(type_id);
2357
2358 tcategory = TypeCategory(type_id);
2359
2360 /*
2361 * We scan through all the opclasses available for the access method,
2362 * looking for one that is marked default and matches the target type
2363 * (either exactly or binary-compatibly, but prefer an exact match).
2364 *
2365 * We could find more than one binary-compatible match. If just one is
2366 * for a preferred type, use that one; otherwise we fail, forcing the user
2367 * to specify which one he wants. (The preferred-type special case is a
2368 * kluge for varchar: it's binary-compatible to both text and bpchar, so
2369 * we need a tiebreaker.) If we find more than one exact match, then
2370 * someone put bogus entries in pg_opclass.
2371 */
2372 rel = table_open(OperatorClassRelationId, AccessShareLock);
2373
2374 ScanKeyInit(&skey[0],
2375 Anum_pg_opclass_opcmethod,
2376 BTEqualStrategyNumber, F_OIDEQ,
2377 ObjectIdGetDatum(am_id));
2378
2379 scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
2380 NULL, 1, skey);
2381
2382 while (HeapTupleIsValid(tup = systable_getnext(scan)))
2383 {
2384 Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
2385
2386 /* ignore altogether if not a default opclass */
2387 if (!opclass->opcdefault)
2388 continue;
2389 if (opclass->opcintype == type_id)
2390 {
2391 nexact++;
2392 result = opclass->oid;
2393 }
2394 else if (nexact == 0 &&
2395 IsBinaryCoercible(type_id, opclass->opcintype))
2396 {
2397 if (IsPreferredType(tcategory, opclass->opcintype))
2398 {
2399 ncompatiblepreferred++;
2400 result = opclass->oid;
2401 }
2402 else if (ncompatiblepreferred == 0)
2403 {
2404 ncompatible++;
2405 result = opclass->oid;
2406 }
2407 }
2408 }
2409
2410 systable_endscan(scan);
2411
2413
2414 /* raise error if pg_opclass contains inconsistent data */
2415 if (nexact > 1)
2416 ereport(ERROR,
2418 errmsg("there are multiple default operator classes for data type %s",
2419 format_type_be(type_id))));
2420
2421 if (nexact == 1 ||
2422 ncompatiblepreferred == 1 ||
2423 (ncompatiblepreferred == 0 && ncompatible == 1))
2424 return result;
2425
2426 return InvalidOid;
2427}
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:603
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:514
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:388
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2604
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:30

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

◆ GetOperatorFromCompareType()

void GetOperatorFromCompareType ( Oid  opclass,
Oid  rhstype,
CompareType  cmptype,
Oid opid,
StrategyNumber strat 
)

Definition at line 2445 of file indexcmds.c.

2447{
2448 Oid amid;
2449 Oid opfamily;
2450 Oid opcintype;
2451
2452 Assert(cmptype == COMPARE_EQ || cmptype == COMPARE_OVERLAP || cmptype == COMPARE_CONTAINED_BY);
2453
2454 amid = get_opclass_method(opclass);
2455
2456 *opid = InvalidOid;
2457
2458 if (get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
2459 {
2460 /*
2461 * Ask the index AM to translate to its internal stratnum
2462 */
2463 *strat = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
2464 if (*strat == InvalidStrategy)
2465 ereport(ERROR,
2466 errcode(ERRCODE_UNDEFINED_OBJECT),
2467 cmptype == COMPARE_EQ ? errmsg("could not identify an equality operator for type %s", format_type_be(opcintype)) :
2468 cmptype == COMPARE_OVERLAP ? errmsg("could not identify an overlaps operator for type %s", format_type_be(opcintype)) :
2469 cmptype == COMPARE_CONTAINED_BY ? errmsg("could not identify a contained-by operator for type %s", format_type_be(opcintype)) : 0,
2470 errdetail("Could not translate compare type %d for operator family \"%s\", input type %s, access method \"%s\".",
2471 cmptype, get_opfamily_name(opfamily, false), format_type_be(opcintype), get_am_name(amid)));
2472
2473 /*
2474 * We parameterize rhstype so foreign keys can ask for a <@ operator
2475 * whose rhs matches the aggregate function. For example range_agg
2476 * returns anymultirange.
2477 */
2478 if (!OidIsValid(rhstype))
2479 rhstype = opcintype;
2480 *opid = get_opfamily_member(opfamily, opcintype, rhstype, *strat);
2481 }
2482
2483 if (!OidIsValid(*opid))
2484 ereport(ERROR,
2485 errcode(ERRCODE_UNDEFINED_OBJECT),
2486 cmptype == COMPARE_EQ ? errmsg("could not identify an equality operator for type %s", format_type_be(opcintype)) :
2487 cmptype == COMPARE_OVERLAP ? errmsg("could not identify an overlaps operator for type %s", format_type_be(opcintype)) :
2488 cmptype == COMPARE_CONTAINED_BY ? errmsg("could not identify a contained-by operator for type %s", format_type_be(opcintype)) : 0,
2489 errdetail("There is no suitable operator in operator family \"%s\" for access method \"%s\".",
2490 get_opfamily_name(opfamily, false), get_am_name(amid)));
2491}
StrategyNumber IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
Definition: amapi.c:143
@ COMPARE_CONTAINED_BY
Definition: cmptype.h:41
Oid get_opclass_method(Oid opclass)
Definition: lsyscache.c:1295
#define InvalidStrategy
Definition: stratnum.h:24

References Assert(), COMPARE_CONTAINED_BY, COMPARE_EQ, COMPARE_OVERLAP, ereport, errcode(), errdetail(), errmsg(), ERROR, format_type_be(), get_am_name(), get_opclass_method(), get_opclass_opfamily_and_input_type(), get_opfamily_member(), get_opfamily_name(), IndexAmTranslateCompareType(), InvalidOid, InvalidStrategy, and OidIsValid.

Referenced by ComputeIndexAttrs(), and FindFKPeriodOpers().

◆ IndexSetParentIndex()

void IndexSetParentIndex ( Relation  partitionIdx,
Oid  parentOid 
)

Definition at line 4407 of file indexcmds.c.

4408{
4409 Relation pg_inherits;
4410 ScanKeyData key[2];
4411 SysScanDesc scan;
4412 Oid partRelid = RelationGetRelid(partitionIdx);
4413 HeapTuple tuple;
4414 bool fix_dependencies;
4415
4416 /* Make sure this is an index */
4417 Assert(partitionIdx->rd_rel->relkind == RELKIND_INDEX ||
4418 partitionIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
4419
4420 /*
4421 * Scan pg_inherits for rows linking our index to some parent.
4422 */
4423 pg_inherits = relation_open(InheritsRelationId, RowExclusiveLock);
4424 ScanKeyInit(&key[0],
4425 Anum_pg_inherits_inhrelid,
4426 BTEqualStrategyNumber, F_OIDEQ,
4427 ObjectIdGetDatum(partRelid));
4428 ScanKeyInit(&key[1],
4429 Anum_pg_inherits_inhseqno,
4430 BTEqualStrategyNumber, F_INT4EQ,
4431 Int32GetDatum(1));
4432 scan = systable_beginscan(pg_inherits, InheritsRelidSeqnoIndexId, true,
4433 NULL, 2, key);
4434 tuple = systable_getnext(scan);
4435
4436 if (!HeapTupleIsValid(tuple))
4437 {
4438 if (parentOid == InvalidOid)
4439 {
4440 /*
4441 * No pg_inherits row, and no parent wanted: nothing to do in this
4442 * case.
4443 */
4444 fix_dependencies = false;
4445 }
4446 else
4447 {
4448 StoreSingleInheritance(partRelid, parentOid, 1);
4449 fix_dependencies = true;
4450 }
4451 }
4452 else
4453 {
4454 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(tuple);
4455
4456 if (parentOid == InvalidOid)
4457 {
4458 /*
4459 * There exists a pg_inherits row, which we want to clear; do so.
4460 */
4461 CatalogTupleDelete(pg_inherits, &tuple->t_self);
4462 fix_dependencies = true;
4463 }
4464 else
4465 {
4466 /*
4467 * A pg_inherits row exists. If it's the same we want, then we're
4468 * good; if it differs, that amounts to a corrupt catalog and
4469 * should not happen.
4470 */
4471 if (inhForm->inhparent != parentOid)
4472 {
4473 /* unexpected: we should not get called in this case */
4474 elog(ERROR, "bogus pg_inherit row: inhrelid %u inhparent %u",
4475 inhForm->inhrelid, inhForm->inhparent);
4476 }
4477
4478 /* already in the right state */
4479 fix_dependencies = false;
4480 }
4481 }
4482
4483 /* done with pg_inherits */
4484 systable_endscan(scan);
4485 relation_close(pg_inherits, RowExclusiveLock);
4486
4487 /* set relhassubclass if an index partition has been added to the parent */
4488 if (OidIsValid(parentOid))
4489 {
4491 SetRelationHasSubclass(parentOid, true);
4492 }
4493
4494 /* set relispartition correctly on the partition */
4495 update_relispartition(partRelid, OidIsValid(parentOid));
4496
4497 if (fix_dependencies)
4498 {
4499 /*
4500 * Insert/delete pg_depend rows. If setting a parent, add PARTITION
4501 * dependencies on the parent index and the table; if removing a
4502 * parent, delete PARTITION dependencies.
4503 */
4504 if (OidIsValid(parentOid))
4505 {
4506 ObjectAddress partIdx;
4507 ObjectAddress parentIdx;
4508 ObjectAddress partitionTbl;
4509
4510 ObjectAddressSet(partIdx, RelationRelationId, partRelid);
4511 ObjectAddressSet(parentIdx, RelationRelationId, parentOid);
4512 ObjectAddressSet(partitionTbl, RelationRelationId,
4513 partitionIdx->rd_index->indrelid);
4514 recordDependencyOn(&partIdx, &parentIdx,
4516 recordDependencyOn(&partIdx, &partitionTbl,
4518 }
4519 else
4520 {
4521 deleteDependencyRecordsForClass(RelationRelationId, partRelid,
4522 RelationRelationId,
4524 deleteDependencyRecordsForClass(RelationRelationId, partRelid,
4525 RelationRelationId,
4527 }
4528
4529 /* make our updates visible */
4531 }
4532}
@ 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:4539
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:365
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:107
static void fix_dependencies(ArchiveHandle *AH)
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:45
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:351
void StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
Definition: pg_inherits.c:508
FormData_pg_inherits * Form_pg_inherits
Definition: pg_inherits.h:45
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:217
#define RelationGetRelid(relation)
Definition: rel.h:513
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:47
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition: tablecmds.c:3608

References Assert(), BTEqualStrategyNumber, CatalogTupleDelete(), CommandCounterIncrement(), deleteDependencyRecordsForClass(), DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, elog, ERROR, fix_dependencies(), GETSTRUCT(), HeapTupleIsValid, Int32GetDatum(), InvalidOid, sort-test::key, LockRelationOid(), ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, RelationData::rd_index, RelationData::rd_rel, recordDependencyOn(), relation_close(), relation_open(), RelationGetRelid, RowExclusiveLock, ScanKeyInit(), SetRelationHasSubclass(), ShareUpdateExclusiveLock, 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 2516 of file indexcmds.c.

2517{
2518 char *name;
2519 int overhead = 0; /* chars needed for label and underscores */
2520 int availchars; /* chars available for name(s) */
2521 int name1chars; /* chars allocated to name1 */
2522 int name2chars; /* chars allocated to name2 */
2523 int ndx;
2524
2525 name1chars = strlen(name1);
2526 if (name2)
2527 {
2528 name2chars = strlen(name2);
2529 overhead++; /* allow for separating underscore */
2530 }
2531 else
2532 name2chars = 0;
2533 if (label)
2534 overhead += strlen(label) + 1;
2535
2536 availchars = NAMEDATALEN - 1 - overhead;
2537 Assert(availchars > 0); /* else caller chose a bad label */
2538
2539 /*
2540 * If we must truncate, preferentially truncate the longer name. This
2541 * logic could be expressed without a loop, but it's simple and obvious as
2542 * a loop.
2543 */
2544 while (name1chars + name2chars > availchars)
2545 {
2546 if (name1chars > name2chars)
2547 name1chars--;
2548 else
2549 name2chars--;
2550 }
2551
2552 name1chars = pg_mbcliplen(name1, name1chars, name1chars);
2553 if (name2)
2554 name2chars = pg_mbcliplen(name2, name2chars, name2chars);
2555
2556 /* Now construct the string using the chosen lengths */
2557 name = palloc(name1chars + name2chars + overhead + 1);
2558 memcpy(name, name1, name1chars);
2559 ndx = name1chars;
2560 if (name2)
2561 {
2562 name[ndx++] = '_';
2563 memcpy(name + ndx, name2, name2chars);
2564 ndx += name2chars;
2565 }
2566 if (label)
2567 {
2568 name[ndx++] = '_';
2569 strcpy(name + ndx, label);
2570 }
2571 else
2572 name[ndx] = '\0';
2573
2574 return name;
2575}
void * palloc(Size size)
Definition: mcxt.c:1317

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

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

◆ RangeVarCallbackForReindexIndex()

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

Definition at line 2937 of file indexcmds.c.

2939{
2940 char relkind;
2942 LOCKMODE table_lockmode;
2943 Oid table_oid;
2944
2945 /*
2946 * Lock level here should match table lock in reindex_index() for
2947 * non-concurrent case and table locks used by index_concurrently_*() for
2948 * concurrent case.
2949 */
2950 table_lockmode = (state->params.options & REINDEXOPT_CONCURRENTLY) != 0 ?
2952
2953 /*
2954 * If we previously locked some other index's heap, and the name we're
2955 * looking up no longer refers to that relation, release the now-useless
2956 * lock.
2957 */
2958 if (relId != oldRelId && OidIsValid(oldRelId))
2959 {
2960 UnlockRelationOid(state->locked_table_oid, table_lockmode);
2961 state->locked_table_oid = InvalidOid;
2962 }
2963
2964 /* If the relation does not exist, there's nothing more to do. */
2965 if (!OidIsValid(relId))
2966 return;
2967
2968 /*
2969 * If the relation does exist, check whether it's an index. But note that
2970 * the relation might have been dropped between the time we did the name
2971 * lookup and now. In that case, there's nothing to do.
2972 */
2973 relkind = get_rel_relkind(relId);
2974 if (!relkind)
2975 return;
2976 if (relkind != RELKIND_INDEX &&
2977 relkind != RELKIND_PARTITIONED_INDEX)
2978 ereport(ERROR,
2979 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2980 errmsg("\"%s\" is not an index", relation->relname)));
2981
2982 /* Check permissions */
2983 table_oid = IndexGetRelation(relId, true);
2984 if (OidIsValid(table_oid))
2985 {
2986 AclResult aclresult;
2987
2988 aclresult = pg_class_aclcheck(table_oid, GetUserId(), ACL_MAINTAIN);
2989 if (aclresult != ACLCHECK_OK)
2990 aclcheck_error(aclresult, OBJECT_INDEX, relation->relname);
2991 }
2992
2993 /* Lock heap before index to avoid deadlock. */
2994 if (relId != oldRelId)
2995 {
2996 /*
2997 * If the OID isn't valid, it means the index was concurrently
2998 * dropped, which is not a problem for us; just return normally.
2999 */
3000 if (OidIsValid(table_oid))
3001 {
3002 LockRelationOid(table_oid, table_lockmode);
3003 state->locked_table_oid = table_oid;
3004 }
3005 }
3006}
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4007
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:229
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2086
#define ACL_MAINTAIN
Definition: parsenodes.h:90
@ OBJECT_INDEX
Definition: parsenodes.h:2332
char * relname
Definition: primnodes.h:83
Definition: regguts.h:323

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

Referenced by ReindexIndex().

◆ reindex_error_callback()

static void reindex_error_callback ( void *  arg)
static

Definition at line 3291 of file indexcmds.c.

3292{
3293 ReindexErrorInfo *errinfo = (ReindexErrorInfo *) arg;
3294
3295 Assert(RELKIND_HAS_PARTITIONS(errinfo->relkind));
3296
3297 if (errinfo->relkind == RELKIND_PARTITIONED_TABLE)
3298 errcontext("while reindexing partitioned table \"%s.%s\"",
3299 errinfo->relnamespace, errinfo->relname);
3300 else if (errinfo->relkind == RELKIND_PARTITIONED_INDEX)
3301 errcontext("while reindexing partitioned index \"%s.%s\"",
3302 errinfo->relnamespace, errinfo->relname);
3303}
#define errcontext
Definition: elog.h:196
char * relnamespace
Definition: indexcmds.c:135

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

Referenced by ReindexPartitions().

◆ ReindexIndex()

static void ReindexIndex ( const ReindexStmt stmt,
const ReindexParams params,
bool  isTopLevel 
)
static

Definition at line 2883 of file indexcmds.c.

2884{
2885 const RangeVar *indexRelation = stmt->relation;
2887 Oid indOid;
2888 char persistence;
2889 char relkind;
2890
2891 /*
2892 * Find and lock index, and check permissions on table; use callback to
2893 * obtain lock on table first, to avoid deadlock hazard. The lock level
2894 * used here must match the index lock obtained in reindex_index().
2895 *
2896 * If it's a temporary index, we will perform a non-concurrent reindex,
2897 * even if CONCURRENTLY was requested. In that case, reindex_index() will
2898 * upgrade the lock, but that's OK, because other sessions can't hold
2899 * locks on our temporary table.
2900 */
2901 state.params = *params;
2902 state.locked_table_oid = InvalidOid;
2903 indOid = RangeVarGetRelidExtended(indexRelation,
2906 0,
2908 &state);
2909
2910 /*
2911 * Obtain the current persistence and kind of the existing index. We
2912 * already hold a lock on the index.
2913 */
2914 persistence = get_rel_persistence(indOid);
2915 relkind = get_rel_relkind(indOid);
2916
2917 if (relkind == RELKIND_PARTITIONED_INDEX)
2918 ReindexPartitions(stmt, indOid, params, isTopLevel);
2919 else if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
2920 persistence != RELPERSISTENCE_TEMP)
2922 else
2923 {
2924 ReindexParams newparams = *params;
2925
2927 reindex_index(stmt, indOid, false, persistence, &newparams);
2928 }
2929}
void reindex_index(const ReindexStmt *stmt, Oid indexId, bool skip_constraint_checks, char persistence, const ReindexParams *params)
Definition: index.c:3608
#define REINDEXOPT_REPORT_PROGRESS
Definition: index.h:42
static bool ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const ReindexParams *params)
Definition: indexcmds.c:3532
static void ReindexPartitions(const ReindexStmt *stmt, Oid relid, const ReindexParams *params, bool isTopLevel)
Definition: indexcmds.c:3312
static void RangeVarCallbackForReindexIndex(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: indexcmds.c:2937
#define AccessExclusiveLock
Definition: lockdefs.h:43
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:441
ReindexParams params
Definition: indexcmds.c:125

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

Referenced by ExecReindex().

◆ ReindexMultipleInternal()

static void ReindexMultipleInternal ( const ReindexStmt stmt,
const List relids,
const ReindexParams params 
)
static

Definition at line 3406 of file indexcmds.c.

3407{
3408 ListCell *l;
3409
3412
3413 foreach(l, relids)
3414 {
3415 Oid relid = lfirst_oid(l);
3416 char relkind;
3417 char relpersistence;
3418
3420
3421 /* functions in indexes may want a snapshot set */
3423
3424 /* check if the relation still exists */
3425 if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
3426 {
3429 continue;
3430 }
3431
3432 /*
3433 * Check permissions except when moving to database's default if a new
3434 * tablespace is chosen. Note that this check also happens in
3435 * ExecReindex(), but we do an extra check here as this runs across
3436 * multiple transactions.
3437 */
3438 if (OidIsValid(params->tablespaceOid) &&
3440 {
3441 AclResult aclresult;
3442
3443 aclresult = object_aclcheck(TableSpaceRelationId, params->tablespaceOid,
3445 if (aclresult != ACLCHECK_OK)
3448 }
3449
3450 relkind = get_rel_relkind(relid);
3451 relpersistence = get_rel_persistence(relid);
3452
3453 /*
3454 * Partitioned tables and indexes can never be processed directly, and
3455 * a list of their leaves should be built first.
3456 */
3457 Assert(!RELKIND_HAS_PARTITIONS(relkind));
3458
3459 if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
3460 relpersistence != RELPERSISTENCE_TEMP)
3461 {
3462 ReindexParams newparams = *params;
3463
3464 newparams.options |= REINDEXOPT_MISSING_OK;
3465 (void) ReindexRelationConcurrently(stmt, relid, &newparams);
3466 if (ActiveSnapshotSet())
3468 /* ReindexRelationConcurrently() does the verbose output */
3469 }
3470 else if (relkind == RELKIND_INDEX)
3471 {
3472 ReindexParams newparams = *params;
3473
3474 newparams.options |=
3476 reindex_index(stmt, relid, false, relpersistence, &newparams);
3478 /* reindex_index() does the verbose output */
3479 }
3480 else
3481 {
3482 bool result;
3483 ReindexParams newparams = *params;
3484
3485 newparams.options |=
3487 result = reindex_relation(stmt, relid,
3490 &newparams);
3491
3492 if (result && (params->options & REINDEXOPT_VERBOSE) != 0)
3493 ereport(INFO,
3494 (errmsg("table \"%s.%s\" was reindexed",
3496 get_rel_name(relid))));
3497
3499 }
3500
3502 }
3503
3505}
#define INFO
Definition: elog.h:34
bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params)
Definition: index.c:3948
#define REINDEX_REL_PROCESS_TOAST
Definition: index.h:159
#define REINDEXOPT_MISSING_OK
Definition: index.h:43
#define REINDEX_REL_CHECK_CONSTRAINTS
Definition: index.h:161
char * get_rel_name(Oid relid)
Definition: lsyscache.c:2011
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:2035
bool ActiveSnapshotSet(void)
Definition: snapmgr.c:799
#define SearchSysCacheExists1(cacheId, key1)
Definition: syscache.h:100

References ACL_CREATE, aclcheck_error(), ACLCHECK_OK, ActiveSnapshotSet(), 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_aclcheck(), OBJECT_TABLESPACE, ObjectIdGetDatum(), OidIsValid, ReindexParams::options, ReindexIndexCallbackState::params, 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(), SearchSysCacheExists1, StartTransactionCommand(), stmt, and ReindexParams::tablespaceOid.

Referenced by ReindexMultipleTables(), and ReindexPartitions().

◆ ReindexMultipleTables()

static void ReindexMultipleTables ( const ReindexStmt stmt,
const ReindexParams params 
)
static

Definition at line 3072 of file indexcmds.c.

3073{
3074
3075 Oid objectOid;
3076 Relation relationRelation;
3077 TableScanDesc scan;
3078 ScanKeyData scan_keys[1];
3079 HeapTuple tuple;
3080 MemoryContext private_context;
3081 MemoryContext old;
3082 List *relids = NIL;
3083 int num_keys;
3084 bool concurrent_warning = false;
3085 bool tablespace_warning = false;
3086 const char *objectName = stmt->name;
3087 const ReindexObjectType objectKind = stmt->kind;
3088
3089 Assert(objectKind == REINDEX_OBJECT_SCHEMA ||
3090 objectKind == REINDEX_OBJECT_SYSTEM ||
3091 objectKind == REINDEX_OBJECT_DATABASE);
3092
3093 /*
3094 * This matches the options enforced by the grammar, where the object name
3095 * is optional for DATABASE and SYSTEM.
3096 */
3097 Assert(objectName || objectKind != REINDEX_OBJECT_SCHEMA);
3098
3099 if (objectKind == REINDEX_OBJECT_SYSTEM &&
3100 (params->options & REINDEXOPT_CONCURRENTLY) != 0)
3101 ereport(ERROR,
3102 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3103 errmsg("cannot reindex system catalogs concurrently")));
3104
3105 /*
3106 * Get OID of object to reindex, being the database currently being used
3107 * by session for a database or for system catalogs, or the schema defined
3108 * by caller. At the same time do permission checks that need different
3109 * processing depending on the object type.
3110 */
3111 if (objectKind == REINDEX_OBJECT_SCHEMA)
3112 {
3113 objectOid = get_namespace_oid(objectName, false);
3114
3115 if (!object_ownercheck(NamespaceRelationId, objectOid, GetUserId()) &&
3116 !has_privs_of_role(GetUserId(), ROLE_PG_MAINTAIN))
3118 objectName);
3119 }
3120 else
3121 {
3122 objectOid = MyDatabaseId;
3123
3124 if (objectName && strcmp(objectName, get_database_name(objectOid)) != 0)
3125 ereport(ERROR,
3126 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3127 errmsg("can only reindex the currently open database")));
3128 if (!object_ownercheck(DatabaseRelationId, objectOid, GetUserId()) &&
3129 !has_privs_of_role(GetUserId(), ROLE_PG_MAINTAIN))
3131 get_database_name(objectOid));
3132 }
3133
3134 /*
3135 * Create a memory context that will survive forced transaction commits we
3136 * do below. Since it is a child of PortalContext, it will go away
3137 * eventually even if we suffer an error; there's no need for special
3138 * abort cleanup logic.
3139 */
3140 private_context = AllocSetContextCreate(PortalContext,
3141 "ReindexMultipleTables",
3143
3144 /*
3145 * Define the search keys to find the objects to reindex. For a schema, we
3146 * select target relations using relnamespace, something not necessary for
3147 * a database-wide operation.
3148 */
3149 if (objectKind == REINDEX_OBJECT_SCHEMA)
3150 {
3151 num_keys = 1;
3152 ScanKeyInit(&scan_keys[0],
3153 Anum_pg_class_relnamespace,
3154 BTEqualStrategyNumber, F_OIDEQ,
3155 ObjectIdGetDatum(objectOid));
3156 }
3157 else
3158 num_keys = 0;
3159
3160 /*
3161 * Scan pg_class to build a list of the relations we need to reindex.
3162 *
3163 * We only consider plain relations and materialized views here (toast
3164 * rels will be processed indirectly by reindex_relation).
3165 */
3166 relationRelation = table_open(RelationRelationId, AccessShareLock);
3167 scan = table_beginscan_catalog(relationRelation, num_keys, scan_keys);
3168 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
3169 {
3170 Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
3171 Oid relid = classtuple->oid;
3172
3173 /*
3174 * Only regular tables and matviews can have indexes, so ignore any
3175 * other kind of relation.
3176 *
3177 * Partitioned tables/indexes are skipped but matching leaf partitions
3178 * are processed.
3179 */
3180 if (classtuple->relkind != RELKIND_RELATION &&
3181 classtuple->relkind != RELKIND_MATVIEW)
3182 continue;
3183
3184 /* Skip temp tables of other backends; we can't reindex them at all */
3185 if (classtuple->relpersistence == RELPERSISTENCE_TEMP &&
3186 !isTempNamespace(classtuple->relnamespace))
3187 continue;
3188
3189 /*
3190 * Check user/system classification. SYSTEM processes all the
3191 * catalogs, and DATABASE processes everything that's not a catalog.
3192 */
3193 if (objectKind == REINDEX_OBJECT_SYSTEM &&
3194 !IsCatalogRelationOid(relid))
3195 continue;
3196 else if (objectKind == REINDEX_OBJECT_DATABASE &&
3197 IsCatalogRelationOid(relid))
3198 continue;
3199
3200 /*
3201 * We already checked privileges on the database or schema, but we
3202 * further restrict reindexing shared catalogs to roles with the
3203 * MAINTAIN privilege on the relation.
3204 */
3205 if (classtuple->relisshared &&
3207 continue;
3208
3209 /*
3210 * Skip system tables, since index_create() would reject indexing them
3211 * concurrently (and it would likely fail if we tried).
3212 */
3213 if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
3214 IsCatalogRelationOid(relid))
3215 {
3216 if (!concurrent_warning)
3218 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3219 errmsg("cannot reindex system catalogs concurrently, skipping all")));
3220 concurrent_warning = true;
3221 continue;
3222 }
3223
3224 /*
3225 * If a new tablespace is set, check if this relation has to be
3226 * skipped.
3227 */
3228 if (OidIsValid(params->tablespaceOid))
3229 {
3230 bool skip_rel = false;
3231
3232 /*
3233 * Mapped relations cannot be moved to different tablespaces (in
3234 * particular this eliminates all shared catalogs.).
3235 */
3236 if (RELKIND_HAS_STORAGE(classtuple->relkind) &&
3237 !RelFileNumberIsValid(classtuple->relfilenode))
3238 skip_rel = true;
3239
3240 /*
3241 * A system relation is always skipped, even with
3242 * allow_system_table_mods enabled.
3243 */
3244 if (IsSystemClass(relid, classtuple))
3245 skip_rel = true;
3246
3247 if (skip_rel)
3248 {
3249 if (!tablespace_warning)
3251 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3252 errmsg("cannot move system relations, skipping all")));
3253 tablespace_warning = true;
3254 continue;
3255 }
3256 }
3257
3258 /* Save the list of relation OIDs in private context */
3259 old = MemoryContextSwitchTo(private_context);
3260
3261 /*
3262 * We always want to reindex pg_class first if it's selected to be
3263 * reindexed. This ensures that if there is any corruption in
3264 * pg_class' indexes, they will be fixed before we process any other
3265 * tables. This is critical because reindexing itself will try to
3266 * update pg_class.
3267 */
3268 if (relid == RelationRelationId)
3269 relids = lcons_oid(relid, relids);
3270 else
3271 relids = lappend_oid(relids, relid);
3272
3274 }
3275 table_endscan(scan);
3276 table_close(relationRelation, AccessShareLock);
3277
3278 /*
3279 * Process each relation listed in a separate transaction. Note that this
3280 * commits and then starts a new transaction immediately.
3281 */
3282 ReindexMultipleInternal(stmt, relids, params);
3283
3284 MemoryContextDelete(private_context);
3285}
bool has_privs_of_role(Oid member, Oid role)
Definition: acl.c:5268
@ ACLCHECK_NOT_OWNER
Definition: acl.h:185
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4058
bool IsCatalogRelationOid(Oid relid)
Definition: catalog.c:120
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition: catalog.c:85
char * get_database_name(Oid dbid)
Definition: dbcommands.c:3188
#define WARNING
Definition: elog.h:36
Oid MyDatabaseId
Definition: globals.c:93
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1347
static void ReindexMultipleInternal(const ReindexStmt *stmt, const List *relids, const ReindexParams *params)
Definition: indexcmds.c:3406
List * lcons_oid(Oid datum, List *list)
Definition: list.c:531
List * lappend_oid(List *list, Oid datum)
Definition: list.c:375
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
MemoryContext PortalContext
Definition: mcxt.c:158
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:170
bool isTempNamespace(Oid namespaceId)
Definition: namespace.c:3649
Oid get_namespace_oid(const char *nspname, bool missing_ok)
Definition: namespace.c:3535
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
@ OBJECT_DATABASE
Definition: parsenodes.h:2321
ReindexObjectType
Definition: parsenodes.h:4055
FormData_pg_class * Form_pg_class
Definition: pg_class.h:156
@ ForwardScanDirection
Definition: sdir.h:28
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, struct ScanKeyData *key)
Definition: tableam.c:113
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:989

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

Referenced by ExecReindex().

◆ ReindexPartitions()

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

Definition at line 3312 of file indexcmds.c.

3313{
3314 List *partitions = NIL;
3315 char relkind = get_rel_relkind(relid);
3316 char *relname = get_rel_name(relid);
3317 char *relnamespace = get_namespace_name(get_rel_namespace(relid));
3318 MemoryContext reindex_context;
3319 List *inhoids;
3320 ListCell *lc;
3321 ErrorContextCallback errcallback;
3322 ReindexErrorInfo errinfo;
3323
3324 Assert(RELKIND_HAS_PARTITIONS(relkind));
3325
3326 /*
3327 * Check if this runs in a transaction block, with an error callback to
3328 * provide more context under which a problem happens.
3329 */
3330 errinfo.relname = pstrdup(relname);
3331 errinfo.relnamespace = pstrdup(relnamespace);
3332 errinfo.relkind = relkind;
3333 errcallback.callback = reindex_error_callback;
3334 errcallback.arg = &errinfo;
3335 errcallback.previous = error_context_stack;
3336 error_context_stack = &errcallback;
3337
3338 PreventInTransactionBlock(isTopLevel,
3339 relkind == RELKIND_PARTITIONED_TABLE ?
3340 "REINDEX TABLE" : "REINDEX INDEX");
3341
3342 /* Pop the error context stack */
3343 error_context_stack = errcallback.previous;
3344
3345 /*
3346 * Create special memory context for cross-transaction storage.
3347 *
3348 * Since it is a child of PortalContext, it will go away eventually even
3349 * if we suffer an error so there is no need for special abort cleanup
3350 * logic.
3351 */
3352 reindex_context = AllocSetContextCreate(PortalContext, "Reindex",
3354
3355 /* ShareLock is enough to prevent schema modifications */
3356 inhoids = find_all_inheritors(relid, ShareLock, NULL);
3357
3358 /*
3359 * The list of relations to reindex are the physical partitions of the
3360 * tree so discard any partitioned table or index.
3361 */
3362 foreach(lc, inhoids)
3363 {
3364 Oid partoid = lfirst_oid(lc);
3365 char partkind = get_rel_relkind(partoid);
3366 MemoryContext old_context;
3367
3368 /*
3369 * This discards partitioned tables, partitioned indexes and foreign
3370 * tables.
3371 */
3372 if (!RELKIND_HAS_STORAGE(partkind))
3373 continue;
3374
3375 Assert(partkind == RELKIND_INDEX ||
3376 partkind == RELKIND_RELATION);
3377
3378 /* Save partition OID */
3379 old_context = MemoryContextSwitchTo(reindex_context);
3380 partitions = lappend_oid(partitions, partoid);
3381 MemoryContextSwitchTo(old_context);
3382 }
3383
3384 /*
3385 * Process each partition listed in a separate transaction. Note that
3386 * this commits and then starts a new transaction immediately.
3387 */
3389
3390 /*
3391 * Clean up working storage --- note we must do this after
3392 * StartTransactionCommand, else we might be trying to delete the active
3393 * context!
3394 */
3395 MemoryContextDelete(reindex_context);
3396}
ErrorContextCallback * error_context_stack
Definition: elog.c:94
static void reindex_error_callback(void *arg)
Definition: indexcmds.c:3291
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
static int partitions
Definition: pgbench.c:224
struct ErrorContextCallback * previous
Definition: elog.h:296
void(* callback)(void *arg)
Definition: elog.h:297

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, ShareLock, and stmt.

Referenced by ReindexIndex(), and ReindexTable().

◆ ReindexRelationConcurrently()

static bool ReindexRelationConcurrently ( const ReindexStmt stmt,
Oid  relationOid,
const ReindexParams params 
)
static

Definition at line 3532 of file indexcmds.c.

3533{
3534 typedef struct ReindexIndexInfo
3535 {
3536 Oid indexId;
3537 Oid tableId;
3538 Oid amId;
3539 bool safe; /* for set_indexsafe_procflags */
3540 } ReindexIndexInfo;
3541 List *heapRelationIds = NIL;
3542 List *indexIds = NIL;
3543 List *newIndexIds = NIL;
3544 List *relationLocks = NIL;
3545 List *lockTags = NIL;
3546 ListCell *lc,
3547 *lc2;
3548 MemoryContext private_context;
3549 MemoryContext oldcontext;
3550 char relkind;
3551 char *relationName = NULL;
3552 char *relationNamespace = NULL;
3553 PGRUsage ru0;
3554 const int progress_index[] = {
3559 };
3560 int64 progress_vals[4];
3561
3562 /*
3563 * Create a memory context that will survive forced transaction commits we
3564 * do below. Since it is a child of PortalContext, it will go away
3565 * eventually even if we suffer an error; there's no need for special
3566 * abort cleanup logic.
3567 */
3568 private_context = AllocSetContextCreate(PortalContext,
3569 "ReindexConcurrent",
3571
3572 if ((params->options & REINDEXOPT_VERBOSE) != 0)
3573 {
3574 /* Save data needed by REINDEX VERBOSE in private context */
3575 oldcontext = MemoryContextSwitchTo(private_context);
3576
3577 relationName = get_rel_name(relationOid);
3578 relationNamespace = get_namespace_name(get_rel_namespace(relationOid));
3579
3580 pg_rusage_init(&ru0);
3581
3582 MemoryContextSwitchTo(oldcontext);
3583 }
3584
3585 relkind = get_rel_relkind(relationOid);
3586
3587 /*
3588 * Extract the list of indexes that are going to be rebuilt based on the
3589 * relation Oid given by caller.
3590 */
3591 switch (relkind)
3592 {
3593 case RELKIND_RELATION:
3594 case RELKIND_MATVIEW:
3595 case RELKIND_TOASTVALUE:
3596 {
3597 /*
3598 * In the case of a relation, find all its indexes including
3599 * toast indexes.
3600 */
3601 Relation heapRelation;
3602
3603 /* Save the list of relation OIDs in private context */
3604 oldcontext = MemoryContextSwitchTo(private_context);
3605
3606 /* Track this relation for session locks */
3607 heapRelationIds = lappend_oid(heapRelationIds, relationOid);
3608
3609 MemoryContextSwitchTo(oldcontext);
3610
3611 if (IsCatalogRelationOid(relationOid))
3612 ereport(ERROR,
3613 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3614 errmsg("cannot reindex system catalogs concurrently")));
3615
3616 /* Open relation to get its indexes */
3617 if ((params->options & REINDEXOPT_MISSING_OK) != 0)
3618 {
3619 heapRelation = try_table_open(relationOid,
3621 /* leave if relation does not exist */
3622 if (!heapRelation)
3623 break;
3624 }
3625 else
3626 heapRelation = table_open(relationOid,
3628
3629 if (OidIsValid(params->tablespaceOid) &&
3630 IsSystemRelation(heapRelation))
3631 ereport(ERROR,
3632 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3633 errmsg("cannot move system relation \"%s\"",
3634 RelationGetRelationName(heapRelation))));
3635
3636 /* Add all the valid indexes of relation to list */
3637 foreach(lc, RelationGetIndexList(heapRelation))
3638 {
3639 Oid cellOid = lfirst_oid(lc);
3640 Relation indexRelation = index_open(cellOid,
3642
3643 if (!indexRelation->rd_index->indisvalid)
3645 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3646 errmsg("skipping reindex of invalid index \"%s.%s\"",
3648 get_rel_name(cellOid)),
3649 errhint("Use DROP INDEX or REINDEX INDEX.")));
3650 else if (indexRelation->rd_index->indisexclusion)
3652 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3653 errmsg("cannot reindex exclusion constraint index \"%s.%s\" concurrently, skipping",
3655 get_rel_name(cellOid))));
3656 else
3657 {
3658 ReindexIndexInfo *idx;
3659
3660 /* Save the list of relation OIDs in private context */
3661 oldcontext = MemoryContextSwitchTo(private_context);
3662
3663 idx = palloc_object(ReindexIndexInfo);
3664 idx->indexId = cellOid;
3665 /* other fields set later */
3666
3667 indexIds = lappend(indexIds, idx);
3668
3669 MemoryContextSwitchTo(oldcontext);
3670 }
3671
3672 index_close(indexRelation, NoLock);
3673 }
3674
3675 /* Also add the toast indexes */
3676 if (OidIsValid(heapRelation->rd_rel->reltoastrelid))
3677 {
3678 Oid toastOid = heapRelation->rd_rel->reltoastrelid;
3679 Relation toastRelation = table_open(toastOid,
3681
3682 /* Save the list of relation OIDs in private context */
3683 oldcontext = MemoryContextSwitchTo(private_context);
3684
3685 /* Track this relation for session locks */
3686 heapRelationIds = lappend_oid(heapRelationIds, toastOid);
3687
3688 MemoryContextSwitchTo(oldcontext);
3689
3690 foreach(lc2, RelationGetIndexList(toastRelation))
3691 {
3692 Oid cellOid = lfirst_oid(lc2);
3693 Relation indexRelation = index_open(cellOid,
3695
3696 if (!indexRelation->rd_index->indisvalid)
3698 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3699 errmsg("skipping reindex of invalid index \"%s.%s\"",
3701 get_rel_name(cellOid)),
3702 errhint("Use DROP INDEX or REINDEX INDEX.")));
3703 else
3704 {
3705 ReindexIndexInfo *idx;
3706
3707 /*
3708 * Save the list of relation OIDs in private
3709 * context
3710 */
3711 oldcontext = MemoryContextSwitchTo(private_context);
3712
3713 idx = palloc_object(ReindexIndexInfo);
3714 idx->indexId = cellOid;
3715 indexIds = lappend(indexIds, idx);
3716 /* other fields set later */
3717
3718 MemoryContextSwitchTo(oldcontext);
3719 }
3720
3721 index_close(indexRelation, NoLock);
3722 }
3723
3724 table_close(toastRelation, NoLock);
3725 }
3726
3727 table_close(heapRelation, NoLock);
3728 break;
3729 }
3730 case RELKIND_INDEX:
3731 {
3732 Oid heapId = IndexGetRelation(relationOid,
3733 (params->options & REINDEXOPT_MISSING_OK) != 0);
3734 Relation heapRelation;
3735 ReindexIndexInfo *idx;
3736
3737 /* if relation is missing, leave */
3738 if (!OidIsValid(heapId))
3739 break;
3740
3741 if (IsCatalogRelationOid(heapId))
3742 ereport(ERROR,
3743 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3744 errmsg("cannot reindex system catalogs concurrently")));
3745
3746 /*
3747 * Don't allow reindex for an invalid index on TOAST table, as
3748 * if rebuilt it would not be possible to drop it. Match
3749 * error message in reindex_index().
3750 */
3751 if (IsToastNamespace(get_rel_namespace(relationOid)) &&
3752 !get_index_isvalid(relationOid))
3753 ereport(ERROR,
3754 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3755 errmsg("cannot reindex invalid index on TOAST table")));
3756
3757 /*
3758 * Check if parent relation can be locked and if it exists,
3759 * this needs to be done at this stage as the list of indexes
3760 * to rebuild is not complete yet, and REINDEXOPT_MISSING_OK
3761 * should not be used once all the session locks are taken.
3762 */
3763 if ((params->options & REINDEXOPT_MISSING_OK) != 0)
3764 {
3765 heapRelation = try_table_open(heapId,
3767 /* leave if relation does not exist */
3768 if (!heapRelation)
3769 break;
3770 }
3771 else
3772 heapRelation = table_open(heapId,
3774
3775 if (OidIsValid(params->tablespaceOid) &&
3776 IsSystemRelation(heapRelation))
3777 ereport(ERROR,
3778 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3779 errmsg("cannot move system relation \"%s\"",
3780 get_rel_name(relationOid))));
3781
3782 table_close(heapRelation, NoLock);
3783
3784 /* Save the list of relation OIDs in private context */
3785 oldcontext = MemoryContextSwitchTo(private_context);
3786
3787 /* Track the heap relation of this index for session locks */
3788 heapRelationIds = list_make1_oid(heapId);
3789
3790 /*
3791 * Save the list of relation OIDs in private context. Note
3792 * that invalid indexes are allowed here.
3793 */
3794 idx = palloc_object(ReindexIndexInfo);
3795 idx->indexId = relationOid;
3796 indexIds = lappend(indexIds, idx);
3797 /* other fields set later */
3798
3799 MemoryContextSwitchTo(oldcontext);
3800 break;
3801 }
3802
3803 case RELKIND_PARTITIONED_TABLE:
3804 case RELKIND_PARTITIONED_INDEX:
3805 default:
3806 /* Return error if type of relation is not supported */
3807 ereport(ERROR,
3808 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3809 errmsg("cannot reindex this type of relation concurrently")));
3810 break;
3811 }
3812
3813 /*
3814 * Definitely no indexes, so leave. Any checks based on
3815 * REINDEXOPT_MISSING_OK should be done only while the list of indexes to
3816 * work on is built as the session locks taken before this transaction
3817 * commits will make sure that they cannot be dropped by a concurrent
3818 * session until this operation completes.
3819 */
3820 if (indexIds == NIL)
3821 return false;
3822
3823 /* It's not a shared catalog, so refuse to move it to shared tablespace */
3824 if (params->tablespaceOid == GLOBALTABLESPACE_OID)
3825 ereport(ERROR,
3826 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3827 errmsg("cannot move non-shared relation to tablespace \"%s\"",
3829
3830 Assert(heapRelationIds != NIL);
3831
3832 /*-----
3833 * Now we have all the indexes we want to process in indexIds.
3834 *
3835 * The phases now are:
3836 *
3837 * 1. create new indexes in the catalog
3838 * 2. build new indexes
3839 * 3. let new indexes catch up with tuples inserted in the meantime
3840 * 4. swap index names
3841 * 5. mark old indexes as dead
3842 * 6. drop old indexes
3843 *
3844 * We process each phase for all indexes before moving to the next phase,
3845 * for efficiency.
3846 */
3847
3848 /*
3849 * Phase 1 of REINDEX CONCURRENTLY
3850 *
3851 * Create a new index with the same properties as the old one, but it is
3852 * only registered in catalogs and will be built later. Then get session
3853 * locks on all involved tables. See analogous code in DefineIndex() for
3854 * more detailed comments.
3855 */
3856
3857 foreach(lc, indexIds)
3858 {
3859 char *concurrentName;
3860 ReindexIndexInfo *idx = lfirst(lc);
3861 ReindexIndexInfo *newidx;
3862 Oid newIndexId;
3863 Relation indexRel;
3864 Relation heapRel;
3865 Oid save_userid;
3866 int save_sec_context;
3867 int save_nestlevel;
3868 Relation newIndexRel;
3869 LockRelId *lockrelid;
3870 Oid tablespaceid;
3871
3872 indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock);
3873 heapRel = table_open(indexRel->rd_index->indrelid,
3875
3876 /*
3877 * Switch to the table owner's userid, so that any index functions are
3878 * run as that user. Also lock down security-restricted operations
3879 * and arrange to make GUC variable changes local to this command.
3880 */
3881 GetUserIdAndSecContext(&save_userid, &save_sec_context);
3882 SetUserIdAndSecContext(heapRel->rd_rel->relowner,
3883 save_sec_context | SECURITY_RESTRICTED_OPERATION);
3884 save_nestlevel = NewGUCNestLevel();
3886
3887 /* determine safety of this index for set_indexsafe_procflags */
3888 idx->safe = (RelationGetIndexExpressions(indexRel) == NIL &&
3889 RelationGetIndexPredicate(indexRel) == NIL);
3890
3891#ifdef USE_INJECTION_POINTS
3892 if (idx->safe)
3893 INJECTION_POINT("reindex-conc-index-safe");
3894 else
3895 INJECTION_POINT("reindex-conc-index-not-safe");
3896#endif
3897
3898 idx->tableId = RelationGetRelid(heapRel);
3899 idx->amId = indexRel->rd_rel->relam;
3900
3901 /* This function shouldn't be called for temporary relations. */
3902 if (indexRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
3903 elog(ERROR, "cannot reindex a temporary table concurrently");
3904
3906
3908 progress_vals[1] = 0; /* initializing */
3909 progress_vals[2] = idx->indexId;
3910 progress_vals[3] = idx->amId;
3911 pgstat_progress_update_multi_param(4, progress_index, progress_vals);
3912
3913 /* Choose a temporary relation name for the new index */
3914 concurrentName = ChooseRelationName(get_rel_name(idx->indexId),
3915 NULL,
3916 "ccnew",
3917 get_rel_namespace(indexRel->rd_index->indrelid),
3918 false);
3919
3920 /* Choose the new tablespace, indexes of toast tables are not moved */
3921 if (OidIsValid(params->tablespaceOid) &&
3922 heapRel->rd_rel->relkind != RELKIND_TOASTVALUE)
3923 tablespaceid = params->tablespaceOid;
3924 else
3925 tablespaceid = indexRel->rd_rel->reltablespace;
3926
3927 /* Create new index definition based on given index */
3928 newIndexId = index_concurrently_create_copy(heapRel,
3929 idx->indexId,
3930 tablespaceid,
3931 concurrentName);
3932
3933 /*
3934 * Now open the relation of the new index, a session-level lock is
3935 * also needed on it.
3936 */
3937 newIndexRel = index_open(newIndexId, ShareUpdateExclusiveLock);
3938
3939 /*
3940 * Save the list of OIDs and locks in private context
3941 */
3942 oldcontext = MemoryContextSwitchTo(private_context);
3943
3944 newidx = palloc_object(ReindexIndexInfo);
3945 newidx->indexId = newIndexId;
3946 newidx->safe = idx->safe;
3947 newidx->tableId = idx->tableId;
3948 newidx->amId = idx->amId;
3949
3950 newIndexIds = lappend(newIndexIds, newidx);
3951
3952 /*
3953 * Save lockrelid to protect each relation from drop then close
3954 * relations. The lockrelid on parent relation is not taken here to
3955 * avoid multiple locks taken on the same relation, instead we rely on
3956 * parentRelationIds built earlier.
3957 */
3958 lockrelid = palloc_object(LockRelId);
3959 *lockrelid = indexRel->rd_lockInfo.lockRelId;
3960 relationLocks = lappend(relationLocks, lockrelid);
3961 lockrelid = palloc_object(LockRelId);
3962 *lockrelid = newIndexRel->rd_lockInfo.lockRelId;
3963 relationLocks = lappend(relationLocks, lockrelid);
3964
3965 MemoryContextSwitchTo(oldcontext);
3966
3967 index_close(indexRel, NoLock);
3968 index_close(newIndexRel, NoLock);
3969
3970 /* Roll back any GUC changes executed by index functions */
3971 AtEOXact_GUC(false, save_nestlevel);
3972
3973 /* Restore userid and security context */
3974 SetUserIdAndSecContext(save_userid, save_sec_context);
3975
3976 table_close(heapRel, NoLock);
3977
3978 /*
3979 * If a statement is available, telling that this comes from a REINDEX
3980 * command, collect the new index for event triggers.
3981 */
3982 if (stmt)
3983 {
3984 ObjectAddress address;
3985
3986 ObjectAddressSet(address, RelationRelationId, newIndexId);
3989 (Node *) stmt);
3990 }
3991 }
3992
3993 /*
3994 * Save the heap lock for following visibility checks with other backends
3995 * might conflict with this session.
3996 */
3997 foreach(lc, heapRelationIds)
3998 {
4000 LockRelId *lockrelid;
4001 LOCKTAG *heaplocktag;
4002
4003 /* Save the list of locks in private context */
4004 oldcontext = MemoryContextSwitchTo(private_context);
4005
4006 /* Add lockrelid of heap relation to the list of locked relations */
4007 lockrelid = palloc_object(LockRelId);
4008 *lockrelid = heapRelation->rd_lockInfo.lockRelId;
4009 relationLocks = lappend(relationLocks, lockrelid);
4010
4011 heaplocktag = palloc_object(LOCKTAG);
4012
4013 /* Save the LOCKTAG for this parent relation for the wait phase */
4014 SET_LOCKTAG_RELATION(*heaplocktag, lockrelid->dbId, lockrelid->relId);
4015 lockTags = lappend(lockTags, heaplocktag);
4016
4017 MemoryContextSwitchTo(oldcontext);
4018
4019 /* Close heap relation */
4020 table_close(heapRelation, NoLock);
4021 }
4022
4023 /* Get a session-level lock on each table. */
4024 foreach(lc, relationLocks)
4025 {
4026 LockRelId *lockrelid = (LockRelId *) lfirst(lc);
4027
4029 }
4030
4034
4035 /*
4036 * Because we don't take a snapshot in this transaction, there's no need
4037 * to set the PROC_IN_SAFE_IC flag here.
4038 */
4039
4040 /*
4041 * Phase 2 of REINDEX CONCURRENTLY
4042 *
4043 * Build the new indexes in a separate transaction for each index to avoid
4044 * having open transactions for an unnecessary long time. But before
4045 * doing that, wait until no running transactions could have the table of
4046 * the index open with the old list of indexes. See "phase 2" in
4047 * DefineIndex() for more details.
4048 */
4049
4052 WaitForLockersMultiple(lockTags, ShareLock, true);
4054
4055 foreach(lc, newIndexIds)
4056 {
4057 ReindexIndexInfo *newidx = lfirst(lc);
4058
4059 /* Start new transaction for this index's concurrent build */
4061
4062 /*
4063 * Check for user-requested abort. This is inside a transaction so as
4064 * xact.c does not issue a useless WARNING, and ensures that
4065 * session-level locks are cleaned up on abort.
4066 */
4068
4069 /* Tell concurrent indexing to ignore us, if index qualifies */
4070 if (newidx->safe)
4072
4073 /* Set ActiveSnapshot since functions in the indexes may need it */
4075
4076 /*
4077 * Update progress for the index to build, with the correct parent
4078 * table involved.
4079 */
4082 progress_vals[1] = PROGRESS_CREATEIDX_PHASE_BUILD;
4083 progress_vals[2] = newidx->indexId;
4084 progress_vals[3] = newidx->amId;
4085 pgstat_progress_update_multi_param(4, progress_index, progress_vals);
4086
4087 /* Perform concurrent build of new index */
4088 index_concurrently_build(newidx->tableId, newidx->indexId);
4089
4092 }
4093
4095
4096 /*
4097 * Because we don't take a snapshot or Xid in this transaction, there's no
4098 * need to set the PROC_IN_SAFE_IC flag here.
4099 */
4100
4101 /*
4102 * Phase 3 of REINDEX CONCURRENTLY
4103 *
4104 * During this phase the old indexes catch up with any new tuples that
4105 * were created during the previous phase. See "phase 3" in DefineIndex()
4106 * for more details.
4107 */
4108
4111 WaitForLockersMultiple(lockTags, ShareLock, true);
4113
4114 foreach(lc, newIndexIds)
4115 {
4116 ReindexIndexInfo *newidx = lfirst(lc);
4117 TransactionId limitXmin;
4118 Snapshot snapshot;
4119
4121
4122 /*
4123 * Check for user-requested abort. This is inside a transaction so as
4124 * xact.c does not issue a useless WARNING, and ensures that
4125 * session-level locks are cleaned up on abort.
4126 */
4128
4129 /* Tell concurrent indexing to ignore us, if index qualifies */
4130 if (newidx->safe)
4132
4133 /*
4134 * Take the "reference snapshot" that will be used by validate_index()
4135 * to filter candidate tuples.
4136 */
4138 PushActiveSnapshot(snapshot);
4139
4140 /*
4141 * Update progress for the index to build, with the correct parent
4142 * table involved.
4143 */
4147 progress_vals[2] = newidx->indexId;
4148 progress_vals[3] = newidx->amId;
4149 pgstat_progress_update_multi_param(4, progress_index, progress_vals);
4150
4151 validate_index(newidx->tableId, newidx->indexId, snapshot);
4152
4153 /*
4154 * We can now do away with our active snapshot, we still need to save
4155 * the xmin limit to wait for older snapshots.
4156 */
4157 limitXmin = snapshot->xmin;
4158
4160 UnregisterSnapshot(snapshot);
4161
4162 /*
4163 * To ensure no deadlocks, we must commit and start yet another
4164 * transaction, and do our wait before any snapshot has been taken in
4165 * it.
4166 */
4169
4170 /*
4171 * The index is now valid in the sense that it contains all currently
4172 * interesting tuples. But since it might not contain tuples deleted
4173 * just before the reference snap was taken, we have to wait out any
4174 * transactions that might have older snapshots.
4175 *
4176 * Because we don't take a snapshot or Xid in this transaction,
4177 * there's no need to set the PROC_IN_SAFE_IC flag here.
4178 */
4181 WaitForOlderSnapshots(limitXmin, true);
4182
4184 }
4185
4186 /*
4187 * Phase 4 of REINDEX CONCURRENTLY
4188 *
4189 * Now that the new indexes have been validated, swap each new index with
4190 * its corresponding old index.
4191 *
4192 * We mark the new indexes as valid and the old indexes as not valid at
4193 * the same time to make sure we only get constraint violations from the
4194 * indexes with the correct names.
4195 */
4196
4198
4199 /*
4200 * Because this transaction only does catalog manipulations and doesn't do
4201 * any index operations, we can set the PROC_IN_SAFE_IC flag here
4202 * unconditionally.
4203 */
4205
4206 forboth(lc, indexIds, lc2, newIndexIds)
4207 {
4208 ReindexIndexInfo *oldidx = lfirst(lc);
4209 ReindexIndexInfo *newidx = lfirst(lc2);
4210 char *oldName;
4211
4212 /*
4213 * Check for user-requested abort. This is inside a transaction so as
4214 * xact.c does not issue a useless WARNING, and ensures that
4215 * session-level locks are cleaned up on abort.
4216 */
4218
4219 /* Choose a relation name for old index */
4220 oldName = ChooseRelationName(get_rel_name(oldidx->indexId),
4221 NULL,
4222 "ccold",
4223 get_rel_namespace(oldidx->tableId),
4224 false);
4225
4226 /*
4227 * Updating pg_index might involve TOAST table access, so ensure we
4228 * have a valid snapshot.
4229 */
4231
4232 /*
4233 * Swap old index with the new one. This also marks the new one as
4234 * valid and the old one as not valid.
4235 */
4236 index_concurrently_swap(newidx->indexId, oldidx->indexId, oldName);
4237
4239
4240 /*
4241 * Invalidate the relcache for the table, so that after this commit
4242 * all sessions will refresh any cached plans that might reference the
4243 * index.
4244 */
4245 CacheInvalidateRelcacheByRelid(oldidx->tableId);
4246
4247 /*
4248 * CCI here so that subsequent iterations see the oldName in the
4249 * catalog and can choose a nonconflicting name for their oldName.
4250 * Otherwise, this could lead to conflicts if a table has two indexes
4251 * whose names are equal for the first NAMEDATALEN-minus-a-few
4252 * characters.
4253 */
4255 }
4256
4257 /* Commit this transaction and make index swaps visible */
4260
4261 /*
4262 * While we could set PROC_IN_SAFE_IC if all indexes qualified, there's no
4263 * real need for that, because we only acquire an Xid after the wait is
4264 * done, and that lasts for a very short period.
4265 */
4266
4267 /*
4268 * Phase 5 of REINDEX CONCURRENTLY
4269 *
4270 * Mark the old indexes as dead. First we must wait until no running
4271 * transaction could be using the index for a query. See also
4272 * index_drop() for more details.
4273 */
4274
4278
4279 foreach(lc, indexIds)
4280 {
4281 ReindexIndexInfo *oldidx = lfirst(lc);
4282
4283 /*
4284 * Check for user-requested abort. This is inside a transaction so as
4285 * xact.c does not issue a useless WARNING, and ensures that
4286 * session-level locks are cleaned up on abort.
4287 */
4289
4290 /*
4291 * Updating pg_index might involve TOAST table access, so ensure we
4292 * have a valid snapshot.
4293 */
4295
4296 index_concurrently_set_dead(oldidx->tableId, oldidx->indexId);
4297
4299 }
4300
4301 /* Commit this transaction to make the updates visible. */
4304
4305 /*
4306 * While we could set PROC_IN_SAFE_IC if all indexes qualified, there's no
4307 * real need for that, because we only acquire an Xid after the wait is
4308 * done, and that lasts for a very short period.
4309 */
4310
4311 /*
4312 * Phase 6 of REINDEX CONCURRENTLY
4313 *
4314 * Drop the old indexes.
4315 */
4316
4320
4322
4323 {
4325
4326 foreach(lc, indexIds)
4327 {
4328 ReindexIndexInfo *idx = lfirst(lc);
4329 ObjectAddress object;
4330
4331 object.classId = RelationRelationId;
4332 object.objectId = idx->indexId;
4333 object.objectSubId = 0;
4334
4335 add_exact_object_address(&object, objects);
4336 }
4337
4338 /*
4339 * Use PERFORM_DELETION_CONCURRENT_LOCK so that index_drop() uses the
4340 * right lock level.
4341 */
4344 }
4345
4348
4349 /*
4350 * Finally, release the session-level lock on the table.
4351 */
4352 foreach(lc, relationLocks)
4353 {
4354 LockRelId *lockrelid = (LockRelId *) lfirst(lc);
4355
4357 }
4358
4359 /* Start a new transaction to finish process properly */
4361
4362 /* Log what we did */
4363 if ((params->options & REINDEXOPT_VERBOSE) != 0)
4364 {
4365 if (relkind == RELKIND_INDEX)
4366 ereport(INFO,
4367 (errmsg("index \"%s.%s\" was reindexed",
4368 relationNamespace, relationName),
4369 errdetail("%s.",
4370 pg_rusage_show(&ru0))));
4371 else
4372 {
4373 foreach(lc, newIndexIds)
4374 {
4375 ReindexIndexInfo *idx = lfirst(lc);
4376 Oid indOid = idx->indexId;
4377
4378 ereport(INFO,
4379 (errmsg("index \"%s.%s\" was reindexed",
4381 get_rel_name(indOid))));
4382 /* Don't show rusage here, since it's not per index. */
4383 }
4384
4385 ereport(INFO,
4386 (errmsg("table \"%s.%s\" was reindexed",
4387 relationNamespace, relationName),
4388 errdetail("%s.",
4389 pg_rusage_show(&ru0))));
4390 }
4391 }
4392
4393 MemoryContextDelete(private_context);
4394
4396
4397 return true;
4398}
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
bool IsToastNamespace(Oid namespaceId)
Definition: catalog.c:230
bool IsSystemRelation(Relation relation)
Definition: catalog.c:73
void performMultipleDeletions(const ObjectAddresses *objects, DropBehavior behavior, int flags)
Definition: dependency.c:332
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2548
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2502
#define PERFORM_DELETION_CONCURRENT_LOCK
Definition: dependency.h:97
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:92
void EventTriggerCollectSimpleCommand(ObjectAddress address, ObjectAddress secondaryObject, Node *parsetree)
#define palloc_object(type)
Definition: fe_memutils.h:74
void index_concurrently_set_dead(Oid heapId, Oid indexId)
Definition: index.c:1823
void index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
Definition: index.c:1552
Oid index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, Oid tablespaceOid, const char *newName)
Definition: index.c:1300
#define INJECTION_POINT(name)
void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode, bool progress)
Definition: lmgr.c:905
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
const ObjectAddress InvalidObjectAddress
@ DROP_RESTRICT
Definition: parsenodes.h:2385
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
#define list_make1_oid(x1)
Definition: pg_list.h:242
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:103
#define PROGRESS_CREATEIDX_PHASE_BUILD
Definition: progress.h:97
#define PROGRESS_CREATEIDX_COMMAND_REINDEX_CONCURRENTLY
Definition: progress.h:116
#define PROGRESS_CREATEIDX_PHASE_VALIDATE_IDXSCAN
Definition: progress.h:99
#define PROGRESS_CREATEIDX_PHASE_WAIT_5
Definition: progress.h:104
List * RelationGetIndexPredicate(Relation relation)
Definition: relcache.c:5138
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5025
Relation try_table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:60

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(), errhint(), errmsg(), ERROR, EventTriggerCollectSimpleCommand(), 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, INJECTION_POINT, InvalidObjectAddress, IsCatalogRelationOid(), IsSystemRelation(), IsToastNamespace(), lappend(), lappend_oid(), lfirst, lfirst_oid, list_make1_oid, LockRelationIdForSession(), LockInfoData::lockRelId, MemoryContextDelete(), MemoryContextSwitchTo(), new_object_addresses(), NewGUCNestLevel(), NIL, NoLock, ObjectAddressSet, OidIsValid, ReindexParams::options, palloc_object, 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_lockInfo, RelationData::rd_rel, RegisterSnapshot(), REINDEXOPT_MISSING_OK, REINDEXOPT_VERBOSE, RelationGetIndexExpressions(), RelationGetIndexList(), RelationGetIndexPredicate(), RelationGetRelationName, RelationGetRelid, LockRelId::relId, RestrictSearchPath(), SECURITY_RESTRICTED_OPERATION, set_indexsafe_procflags(), SET_LOCKTAG_RELATION, SetUserIdAndSecContext(), ShareLock, ShareUpdateExclusiveLock, StartTransactionCommand(), stmt, 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 ( const ReindexStmt stmt,
const ReindexParams params,
bool  isTopLevel 
)
static

Definition at line 3013 of file indexcmds.c.

3014{
3015 Oid heapOid;
3016 bool result;
3017 const RangeVar *relation = stmt->relation;
3018
3019 /*
3020 * The lock level used here should match reindex_relation().
3021 *
3022 * If it's a temporary table, we will perform a non-concurrent reindex,
3023 * even if CONCURRENTLY was requested. In that case, reindex_relation()
3024 * will upgrade the lock, but that's OK, because other sessions can't hold
3025 * locks on our temporary table.
3026 */
3027 heapOid = RangeVarGetRelidExtended(relation,
3028 (params->options & REINDEXOPT_CONCURRENTLY) != 0 ?
3030 0,
3032
3033 if (get_rel_relkind(heapOid) == RELKIND_PARTITIONED_TABLE)
3034 ReindexPartitions(stmt, heapOid, params, isTopLevel);
3035 else if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
3036 get_rel_persistence(heapOid) != RELPERSISTENCE_TEMP)
3037 {
3038 result = ReindexRelationConcurrently(stmt, heapOid, params);
3039
3040 if (!result)
3042 (errmsg("table \"%s\" has no indexes that can be reindexed concurrently",
3043 relation->relname)));
3044 }
3045 else
3046 {
3047 ReindexParams newparams = *params;
3048
3050 result = reindex_relation(stmt, heapOid,
3053 &newparams);
3054 if (!result)
3056 (errmsg("table \"%s\" has no indexes to reindex",
3057 relation->relname)));
3058 }
3059
3060 return heapOid;
3061}
void RangeVarCallbackMaintainsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:18761

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

Referenced by ExecReindex().

◆ ResolveOpClass()

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

Definition at line 2258 of file indexcmds.c.

2260{
2261 char *schemaname;
2262 char *opcname;
2263 HeapTuple tuple;
2264 Form_pg_opclass opform;
2265 Oid opClassId,
2266 opInputType;
2267
2268 if (opclass == NIL)
2269 {
2270 /* no operator class specified, so find the default */
2271 opClassId = GetDefaultOpClass(attrType, accessMethodId);
2272 if (!OidIsValid(opClassId))
2273 ereport(ERROR,
2274 (errcode(ERRCODE_UNDEFINED_OBJECT),
2275 errmsg("data type %s has no default operator class for access method \"%s\"",
2276 format_type_be(attrType), accessMethodName),
2277 errhint("You must specify an operator class for the index or define a default operator class for the data type.")));
2278 return opClassId;
2279 }
2280
2281 /*
2282 * Specific opclass name given, so look up the opclass.
2283 */
2284
2285 /* deconstruct the name list */
2286 DeconstructQualifiedName(opclass, &schemaname, &opcname);
2287
2288 if (schemaname)
2289 {
2290 /* Look in specific schema only */
2291 Oid namespaceId;
2292
2293 namespaceId = LookupExplicitNamespace(schemaname, false);
2294 tuple = SearchSysCache3(CLAAMNAMENSP,
2295 ObjectIdGetDatum(accessMethodId),
2296 PointerGetDatum(opcname),
2297 ObjectIdGetDatum(namespaceId));
2298 }
2299 else
2300 {
2301 /* Unqualified opclass name, so search the search path */
2302 opClassId = OpclassnameGetOpcid(accessMethodId, opcname);
2303 if (!OidIsValid(opClassId))
2304 ereport(ERROR,
2305 (errcode(ERRCODE_UNDEFINED_OBJECT),
2306 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
2307 opcname, accessMethodName)));
2308 tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opClassId));
2309 }
2310
2311 if (!HeapTupleIsValid(tuple))
2312 ereport(ERROR,
2313 (errcode(ERRCODE_UNDEFINED_OBJECT),
2314 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
2315 NameListToString(opclass), accessMethodName)));
2316
2317 /*
2318 * Verify that the index operator class accepts this datatype. Note we
2319 * will accept binary compatibility.
2320 */
2321 opform = (Form_pg_opclass) GETSTRUCT(tuple);
2322 opClassId = opform->oid;
2323 opInputType = opform->opcintype;
2324
2325 if (!IsBinaryCoercible(attrType, opInputType))
2326 ereport(ERROR,
2327 (errcode(ERRCODE_DATATYPE_MISMATCH),
2328 errmsg("operator class \"%s\" does not accept data type %s",
2329 NameListToString(opclass), format_type_be(attrType))));
2330
2331 ReleaseSysCache(tuple);
2332
2333 return opClassId;
2334}
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:2343
Oid OpclassnameGetOpcid(Oid amid, const char *opcname)
Definition: namespace.c:2121
char * NameListToString(const List *names)
Definition: namespace.c:3594
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition: namespace.c:3385
void DeconstructQualifiedName(const List *names, char **nspname_p, char **objname_p)
Definition: namespace.c:3301
HeapTuple SearchSysCache3(int cacheId, Datum key1, Datum key2, Datum key3)
Definition: syscache.c:243

References 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 4577 of file indexcmds.c.

4578{
4579 /*
4580 * This should only be called before installing xid or xmin in MyProc;
4581 * otherwise, concurrent processes could see an Xmin that moves backwards.
4582 */
4585
4586 LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
4589 LWLockRelease(ProcArrayLock);
4590}
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1179
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1899
@ LW_EXCLUSIVE
Definition: lwlock.h:114
#define PROC_IN_SAFE_IC
Definition: proc.h:59
PROC_HDR * ProcGlobal
Definition: proc.c:78
uint8 statusFlags
Definition: proc.h:243
int pgxactoff
Definition: proc.h:185
TransactionId xid
Definition: proc.h:173
uint8 * statusFlags
Definition: proc.h:387

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 4539 of file indexcmds.c.

4540{
4541 HeapTuple tup;
4542 Relation classRel;
4543 ItemPointerData otid;
4544
4545 classRel = table_open(RelationRelationId, RowExclusiveLock);
4546 tup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relationId));
4547 if (!HeapTupleIsValid(tup))
4548 elog(ERROR, "cache lookup failed for relation %u", relationId);
4549 otid = tup->t_self;
4550 Assert(((Form_pg_class) GETSTRUCT(tup))->relispartition != newval);
4551 ((Form_pg_class) GETSTRUCT(tup))->relispartition = newval;
4552 CatalogTupleUpdate(classRel, &otid, tup);
4553 UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
4554 heap_freetuple(tup);
4555 table_close(classRel, RowExclusiveLock);
4556}
#define newval
void UnlockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
Definition: lmgr.c:601
#define InplaceUpdateTupleLock
Definition: lockdefs.h:48
HeapTuple SearchSysCacheLockedCopy1(int cacheId, Datum key1)
Definition: syscache.c:404

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

Referenced by IndexSetParentIndex().

◆ WaitForOlderSnapshots()

void WaitForOlderSnapshots ( TransactionId  limitXmin,
bool  progress 
)

Definition at line 435 of file indexcmds.c.

436{
437 int n_old_snapshots;
438 int i;
439 VirtualTransactionId *old_snapshots;
440
441 old_snapshots = GetCurrentVirtualXIDs(limitXmin, true, false,
444 &n_old_snapshots);
445 if (progress)
447
448 for (i = 0; i < n_old_snapshots; i++)
449 {
450 if (!VirtualTransactionIdIsValid(old_snapshots[i]))
451 continue; /* found uninteresting in previous cycle */
452
453 if (i > 0)
454 {
455 /* see if anything's changed ... */
456 VirtualTransactionId *newer_snapshots;
457 int n_newer_snapshots;
458 int j;
459 int k;
460
461 newer_snapshots = GetCurrentVirtualXIDs(limitXmin,
462 true, false,
465 &n_newer_snapshots);
466 for (j = i; j < n_old_snapshots; j++)
467 {
468 if (!VirtualTransactionIdIsValid(old_snapshots[j]))
469 continue; /* found uninteresting in previous cycle */
470 for (k = 0; k < n_newer_snapshots; k++)
471 {
472 if (VirtualTransactionIdEquals(old_snapshots[j],
473 newer_snapshots[k]))
474 break;
475 }
476 if (k >= n_newer_snapshots) /* not there anymore */
477 SetInvalidVirtualTransactionId(old_snapshots[j]);
478 }
479 pfree(newer_snapshots);
480 }
481
482 if (VirtualTransactionIdIsValid(old_snapshots[i]))
483 {
484 /* If requested, publish who we're going to wait for. */
485 if (progress)
486 {
487 PGPROC *holder = ProcNumberGetProc(old_snapshots[i].procNumber);
488
489 if (holder)
491 holder->pid);
492 }
493 VirtualXactLock(old_snapshots[i], true);
494 }
495
496 if (progress)
498 }
499}
bool VirtualXactLock(VirtualTransactionId vxid, bool wait)
Definition: lock.c:4701
#define VirtualTransactionIdIsValid(vxid)
Definition: lock.h:68
#define VirtualTransactionIdEquals(vxid1, vxid2)
Definition: lock.h:72
#define SetInvalidVirtualTransactionId(vxid)
Definition: lock.h:75
static int progress
Definition: pgbench.c:262
#define PROC_IN_VACUUM
Definition: proc.h:58
#define PROC_IS_AUTOVACUUM
Definition: proc.h:57
VirtualTransactionId * GetCurrentVirtualXIDs(TransactionId limitXmin, bool excludeXmin0, bool allDbs, int excludeVacuum, int *nvxids)
Definition: procarray.c:3324
PGPROC * ProcNumberGetProc(ProcNumber procNumber)
Definition: procarray.c:3138
#define PROGRESS_WAITFOR_DONE
Definition: progress.h:120
#define PROGRESS_WAITFOR_TOTAL
Definition: progress.h:119
#define PROGRESS_WAITFOR_CURRENT_PID
Definition: progress.h:121
Definition: proc.h:163
int pid
Definition: proc.h:183

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

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