/* Get the soon-obsolete pg_index tuple. */
tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(oldId)); if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for index %u", oldId);
indexForm = (Form_pg_index) GETSTRUCT(tuple);
/* For polymorphic opcintype, column type changes break compatibility. */
irel = index_open(oldId, AccessShareLock); /* caller probably has a lock */ for (i = 0; i < old_natts; i++)
{ if (IsPolymorphicType(get_opclass_input_type(opclassIds[i])) &&
TupleDescAttr(irel->rd_att, i)->atttypid != typeIds[i])
{
ret = false; break;
}
}
/* Any change in opclass options break compatibility. */ if (ret)
{
Datum *oldOpclassOptions = palloc_array(Datum, old_natts);
for (i = 0; i < old_natts; i++)
oldOpclassOptions[i] = get_attoptions(oldId, i + 1);
ret = CompareOpclassOptions(oldOpclassOptions, opclassOptions, old_natts);
pfree(oldOpclassOptions);
}
/* Any change in exclusion operator selections breaks compatibility. */ if (ret && indexInfo->ii_ExclusionOps != NULL)
{
Oid *old_operators,
*old_procs;
uint16 *old_strats;
/* *CompareOpclassOptions * *Compareper-columnopclassoptionswhicharerepresentedbyarraysoftext[] *datums.BothelementsofarraysandarraythemselvescanbeNULL.
*/ staticbool
CompareOpclassOptions(const Datum *opts1, const Datum *opts2, int natts)
{ int i;
FmgrInfo fm;
if (!opts1 && !opts2) returntrue;
fmgr_info(F_ARRAY_EQ, &fm); for (i = 0; i < natts; i++)
{
Datum opt1 = opts1 ? opts1[i] : (Datum) 0;
Datum opt2 = opts2 ? opts2[i] : (Datum) 0;
for (i = 0; i < n_old_snapshots; i++)
{ if (!VirtualTransactionIdIsValid(old_snapshots[i])) continue; /* found uninteresting in previous cycle */
if (i > 0)
{ /* see if anything's changed ... */
VirtualTransactionId *newer_snapshots; int n_newer_snapshots; int j; int k;
newer_snapshots = GetCurrentVirtualXIDs(limitXmin, true, false,
PROC_IS_AUTOVACUUM | PROC_IN_VACUUM
| PROC_IN_SAFE_IC,
&n_newer_snapshots); for (j = i; j < n_old_snapshots; j++)
{ if (!VirtualTransactionIdIsValid(old_snapshots[j])) continue; /* found uninteresting in previous cycle */ for (k = 0; k < n_newer_snapshots; k++)
{ if (VirtualTransactionIdEquals(old_snapshots[j],
newer_snapshots[k])) break;
} if (k >= n_newer_snapshots) /* not there anymore */
SetInvalidVirtualTransactionId(old_snapshots[j]);
}
pfree(newer_snapshots);
}
if (VirtualTransactionIdIsValid(old_snapshots[i]))
{ /* If requested, publish who we're going to wait for. */ if (progress)
{
PGPROC *holder = ProcNumberGetProc(old_snapshots[i].procNumber);
if (holder)
pgstat_progress_update_param(PROGRESS_WAITFOR_CURRENT_PID,
holder->pid);
}
VirtualXactLock(old_snapshots[i], true);
}
if (progress)
pgstat_progress_update_param(PROGRESS_WAITFOR_DONE, i + 1);
}
}
/* *DefineIndex *Createsanewindex. * *Thisfunctionmanagesthecurrentuseridaccordingtotheneedsofpg_dump. *Recreatingold-databasecatalogentriesinnew-databaseisfine,regardless *ofwhichuserswouldhavepermissiontorecreatethoseentriesnow.That's *justpreservationofstate.Runningopaqueexpressions,likecallinga *functionnamedinacatalogentryorevaluatingapg_node_treeinacatalog *entry,asanyoneotherthantheobjectowner,isnotfine.Toadhereto *thoseprinciplesandtoremainfail-safe,usethetableowneruseridfor *mostACLchecks.UsetheoriginaluseridforACLchecksreachedwithout *traversingopaqueexpressions.(pg_dumpcanpredictsuchACLchecksfrom *catalogs.)Overall,thisisamess.FutureDDLdevelopmentshould *considerofferingoneDDLcommandforcatalogsetupandaseparateDDL *commandforstepsthatrunopaqueexpressions. * *'tableId':theOIDofthetablerelationonwhichtheindexistobe *created *'stmt':IndexStmtdescribingthepropertiesofthenewindex. *'indexRelationId':normallyInvalidOid,butduringbootstrapcanbe *nonzerotospecifyapreselectedOIDfortheindex. *'parentIndexId':theOIDoftheparentindex;InvalidOidifnotthechild *ofapartitionedindex. *'parentConstraintId':theOIDoftheparentconstraint;InvalidOidifnot *thechildofaconstraint(onlyusedwhenrecursing) *'total_parts':totalnumberofdirectandindirectpartitionsofrelation; *pass-1ifnotknownorrelisnotpartitioned. *'is_alter_table':thisisduetoanALTERratherthanaCREATEoperation. *'check_rights':checkforCREATErightsinnamespaceandtablespace.(This *shouldbetrueexceptwhenALTERisdeleting/recreatinganindex.) *'check_not_in_use':checkfortablenotalreadyinuseincurrentsession. *Thisshouldbetrueunlesscallerisholdingthetableopen,inwhich *casethecallerhadbetterhavecheckeditearlier. *'skip_build':makethecatalogentriesbutdon'tcreatetheindexfiles *'quiet':suppresstheNOTICEchatterordinarilyprovidedforconstraints. * *Returnstheobjectaddressofthecreatedindex.
*/
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)
{ bool concurrent; char *indexRelationName; char *accessMethodName;
Oid *typeIds;
Oid *collationIds;
Oid *opclassIds;
Datum *opclassOptions;
Oid accessMethodId;
Oid namespaceId;
Oid tablespaceId;
Oid createdConstraintId = InvalidOid;
List *indexColNames;
List *allIndexParams;
Relation rel;
HeapTuple tuple;
Form_pg_am accessMethodForm;
IndexAmRoutine *amRoutine; bool amcanorder; bool amissummarizing;
amoptions_function amoptions; bool exclusion; bool partitioned; bool safe_index;
Datum reloptions;
int16 *coloptions;
IndexInfo *indexInfo;
bits16 flags;
bits16 constr_flags; int numberOfAttributes; int numberOfKeyAttributes;
TransactionId limitXmin;
ObjectAddress address;
LockRelId heaprelid;
LOCKTAG heaplocktag;
LOCKMODE lockmode;
Snapshot snapshot;
Oid root_save_userid; int root_save_sec_context; int root_save_nestlevel;
if (numberOfKeyAttributes <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("must specify at least one column"))); if (numberOfAttributes > INDEX_MAX_KEYS)
ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_COLUMNS),
errmsg("cannot use more than %d columns in an index",
INDEX_MAX_KEYS)));
/* Ensure that it makes sense to index this kind of relation */ switch (rel->rd_rel->relkind)
{ case RELKIND_RELATION: case RELKIND_MATVIEW: case RELKIND_PARTITIONED_TABLE: /* OK */ break; default:
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot create index on relation \"%s\"",
RelationGetRelationName(rel)),
errdetail_relkind_not_supported(rel->rd_rel->relkind))); break;
}
/* *Establishbehaviorforpartitionedtables,andverifysanityof *parameters. * *Wedonotbuildanactualindexinthiscase;weonlycreateafew *catalogentries.Theactualindexesarebuiltbyrecursingforeach *partition.
*/
partitioned = rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE; if (partitioned)
{ /* *Note:wecheck'stmt->concurrent'ratherthan'concurrent',sothat *theerroristhrownalsofortemporarytables.Seemsbettertobe *consistent,eventhoughwecoulddoitontemporarytablebecause *we'renotactuallydoingitconcurrently.
*/ if (stmt->concurrent)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot create index on partitioned table \"%s\" concurrently",
RelationGetRelationName(rel))));
}
/* *Don'ttrytoCREATEINDEXontemptablesofotherbackends.
*/ if (RELATION_IS_OTHER_TEMP(rel))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot create indexes on temporary tables of other sessions")));
if (stmt->unique && !stmt->iswithoutoverlaps && !amRoutine->amcanunique)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("access method \"%s\" does not support unique indexes",
accessMethodName))); if (stmt->indexIncludingParams != NIL && !amRoutine->amcaninclude)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("access method \"%s\" does not support included columns",
accessMethodName))); if (numberOfKeyAttributes > 1 && !amRoutine->amcanmulticol)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("access method \"%s\" does not support multicolumn indexes",
accessMethodName))); if (exclusion && amRoutine->amgettuple == NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("access method \"%s\" does not support exclusion constraints",
accessMethodName))); if (stmt->iswithoutoverlaps && strcmp(accessMethodName, "gist") != 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("access method \"%s\" does not support WITHOUT OVERLAPS constraints",
accessMethodName)));
/* *Verifythatallthecolumnsinthepartitionkeyappearinthe *uniquekeydefinition,withthesamenotionofequality.
*/ for (i = 0; i < key->partnatts; i++)
{ bool found = false; int eq_strategy;
Oid ptkey_eqop; int j;
ptkey_eqop = get_opfamily_member(key->partopfamily[i],
key->partopcintype[i],
key->partopcintype[i],
eq_strategy); if (!OidIsValid(ptkey_eqop))
elog(ERROR, "missing operator %d(%u,%u) in partition opfamily %u",
eq_strategy, key->partopcintype[i], key->partopcintype[i],
key->partopfamily[i]);
/* *ItmaybepossibletosupportUNIQUEconstraintswhenpartition *keysareexpressions,butisitworthit?Giveupfornow.
*/ if (key->partattrs[i] == 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unsupported %s constraint with partition key definition",
constraint_type),
errdetail("%s constraints cannot be used when partition keys include expressions.",
constraint_type)));
/* Search the index column(s) for a match */ for (j = 0; j < indexInfo->ii_NumIndexKeyAttrs; j++)
{ if (key->partattrs[i] == indexInfo->ii_IndexAttrNumbers[j])
{ /* *Matchedthecolumn,nowwhataboutthecollationand *equalityop?
*/
Oid idx_opfamily;
Oid idx_opcintype;
if (key->partcollation[i] != collationIds[j]) continue;
if (get_opclass_opfamily_and_input_type(opclassIds[j],
&idx_opfamily,
&idx_opcintype))
{
Oid idx_eqop = InvalidOid;
if (!idx_eqop)
ereport(ERROR,
errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not identify an equality operator for type %s", format_type_be(idx_opcintype)),
errdetail("There is no suitable operator in operator family \"%s\" for access method \"%s\".",
get_opfamily_name(idx_opfamily, false), get_am_name(get_opfamily_method(idx_opfamily))));
if (ptkey_eqop == idx_eqop)
{
found = true; break;
} elseif (exclusion)
{ /* *Wefoundamatch,butit'snotanequality *operator.Insteadoffailingbelowwithan *errormessageaboutamissingcolumn,failnow *andexplainthattheoperatoriswrong.
*/
Form_pg_attribute att = TupleDescAttr(RelationGetDescr(rel), key->partattrs[i] - 1);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot match partition key to index on column \"%s\" using non-equal operator \"%s\"",
NameStr(att->attname),
get_opname(indexInfo->ii_ExclusionOps[j]))));
}
}
}
}
if (!found)
{
Form_pg_attribute att;
att = TupleDescAttr(RelationGetDescr(rel),
key->partattrs[i] - 1);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unique constraint on partitioned table must include all partitioning columns"),
errdetail("%s constraint on table \"%s\" lacks column \"%s\" which is part of the partition key.",
constraint_type, RelationGetRelationName(rel),
NameStr(att->attname))));
}
}
}
/* *Wedisallowindexesonsystemcolumns.Theywouldnotnecessarilyget *updatedcorrectly,andtheydon'tseemusefulanyway. * *Alsodisallowvirtualgeneratedcolumnsinindexes(useexpression *indexinstead).
*/ for (int i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
{
AttrNumber attno = indexInfo->ii_IndexAttrNumbers[i];
if (attno < 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("index creation on system columns is not supported")));
if (attno > 0 &&
TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
stmt->primary ?
errmsg("primary keys on virtual generated columns are not supported") :
stmt->isconstraint ?
errmsg("unique constraints on virtual generated columns are not supported") :
errmsg("indexes on virtual generated columns are not supported"));
}
/* *Alsocheckforsystemandgeneratedcolumnsusedinexpressionsor *predicates.
*/ if (indexInfo->ii_Expressions || indexInfo->ii_Predicate)
{
Bitmapset *indexattrs = NULL; int j;
for (int i = FirstLowInvalidHeapAttributeNumber + 1; i < 0; i++)
{ if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
indexattrs))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("index creation on system columns is not supported")));
}
if (attno > 0 &&
TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
stmt->isconstraint ?
errmsg("unique constraints on virtual generated columns are not supported") :
errmsg("indexes on virtual generated columns are not supported")));
}
}
/* Is index safe for others to ignore? See set_indexsafe_procflags() */
safe_index = indexInfo->ii_Expressions == NIL &&
indexInfo->ii_Predicate == NIL;
if (pd->nparts != 0)
flags |= INDEX_CREATE_INVALID;
}
if (stmt->deferrable)
constr_flags |= INDEX_CONSTR_CREATE_DEFERRABLE; if (stmt->initdeferred)
constr_flags |= INDEX_CONSTR_CREATE_INIT_DEFERRED; if (stmt->iswithoutoverlaps)
constr_flags |= INDEX_CONSTR_CREATE_WITHOUT_OVERLAPS;
/* *Foreachpartition,scanallexistingindexes;ifonematches *ourindexdefinitionandisnotalreadyattachedtosomeother *parentindex,attachittotheonewejustcreated. * *Ifnonematches,buildanewindexbycallingourselves *recursivelywiththesameoptions(exceptfortheindexname).
*/ for (int i = 0; i < nparts; i++)
{
Oid childRelid = part_oids[i];
Relation childrel;
Oid child_save_userid; int child_save_sec_context; int child_save_nestlevel;
List *childidxs;
ListCell *cell;
AttrMap *attmap; bool found = false;
/* Attach index to parent and we're done. */
IndexSetParentIndex(cldidx, indexRelationId); if (createdConstraintId != InvalidOid)
ConstraintSetParentConstraint(cldConstrOid,
createdConstraintId,
childRelid);
if (!cldidx->rd_index->indisvalid)
invalidate_parent = true;
if (!concurrent)
{ /* Close the heap and we're done, in the non-concurrent case */
table_close(rel, NoLock);
/* *Ifthisisthetop-levelindex,thecommandisdoneoverall; *otherwise,incrementprogresstoreportonechildindexisdone.
*/ if (!OidIsValid(parentIndexId))
pgstat_progress_end_command(); else
pgstat_progress_incr_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, 1);
return address;
}
/* save lockrelid and locktag for below, then close rel */
heaprelid = rel->rd_lockInfo.lockRelId;
SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId);
table_close(rel, NoLock);
/* *Apredicateusingmutablefunctionsisprobablywrong,forthesame *reasonsthatwedon'tallowanindexexpressiontouseone.
*/ if (contain_mutable_functions_after_planning(predicate))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("functions in index predicate must be marked IMMUTABLE")));
}
/* *Computeper-index-columninformation,includingindexedcolumnnumbers *orindexexpressions,opclassesandtheiroptions.Note,alloutputvectors *shouldbeallocatedforallcolumns,including"including"ones. * *Ifthecallerswitchedtothetableowner,ddl_useridistheroleforACL *checksreachedwithouttraversingopaqueexpressions.Otherwise,it's *InvalidOid,andotherddl_*argumentsareundefined.
*/ staticvoid
ComputeIndexAttrs(IndexInfo *indexInfo,
Oid *typeOids,
Oid *collationOids,
Oid *opclassOids,
Datum *opclassOptions,
int16 *colOptions, const List *attList, /* list of IndexElem's */ const List *exclusionOpNames,
Oid relId, constchar *accessMethodName,
Oid accessMethodId, bool amcanorder, bool isconstraint, bool iswithoutoverlaps,
Oid ddl_userid, int ddl_sec_context, int *ddl_save_nestlevel)
{
ListCell *nextExclOp;
ListCell *lc; int attn; int nkeycols = indexInfo->ii_NumIndexKeyAttrs;
Oid save_userid; int save_sec_context;
/* Allocate space for exclusion operator info, if needed */ if (exclusionOpNames)
{
Assert(list_length(exclusionOpNames) == nkeycols);
indexInfo->ii_ExclusionOps = palloc_array(Oid, nkeycols);
indexInfo->ii_ExclusionProcs = palloc_array(Oid, nkeycols);
indexInfo->ii_ExclusionStrats = palloc_array(uint16, nkeycols);
nextExclOp = list_head(exclusionOpNames);
} else
nextExclOp = NULL;
if (OidIsValid(ddl_userid))
GetUserIdAndSecContext(&save_userid, &save_sec_context);
/* *processattributeList
*/
attn = 0;
foreach(lc, attList)
{
IndexElem *attribute = (IndexElem *) lfirst(lc);
Oid atttype;
Oid attcollation;
/* *Processthecolumn-or-expressiontobeindexed.
*/ if (attribute->name != NULL)
{ /* Simple index attribute */
HeapTuple atttuple;
Form_pg_attribute attform;
Assert(attribute->expr == NULL);
atttuple = SearchSysCacheAttName(relId, attribute->name); if (!HeapTupleIsValid(atttuple))
{ /* difference in error message spellings is historical */ if (isconstraint)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" named in key does not exist",
attribute->name))); else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" does not exist",
attribute->name)));
}
attform = (Form_pg_attribute) GETSTRUCT(atttuple);
indexInfo->ii_IndexAttrNumbers[attn] = attform->attnum;
atttype = attform->atttypid;
attcollation = attform->attcollation;
ReleaseSysCache(atttuple);
} else
{ /* Index expression */
Node *expr = attribute->expr;
Assert(expr != NULL);
if (attn >= nkeycols)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("expressions are not supported in included columns")));
atttype = exprType(expr);
attcollation = exprCollation(expr);
/* *Anexpressionusingmutablefunctionsisprobablywrong, *sinceifyouaren'tgoingtogetthesameresultforthe *samedataeverytime,it'snotclearwhattheindexentries *meanatall.
*/ if (contain_mutable_functions_after_planning((Expr *) expr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("functions in index expression must be marked IMMUTABLE")));
}
}
typeOids[attn] = atttype;
/* *Includedcolumnshavenocollation,noopclassandnoordering *options.
*/ if (attn >= nkeycols)
{ if (attribute->collation)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("including column does not support a collation"))); if (attribute->opclass)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("including column does not support an operator class"))); if (attribute->ordering != SORTBY_DEFAULT)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("including column does not support ASC/DESC options"))); if (attribute->nulls_ordering != SORTBY_NULLS_DEFAULT)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("including column does not support NULLS FIRST/LAST options")));
/* *Checkwehaveacollationiffit'sacollatabletype.Theonly *expectedfailureshereare(1)COLLATEappliedtoanoncollatable *type,or(2)indexexpressionhadanunresolvedcollation.Butwe *mightaswellcodethistobeacompleteconsistencycheck.
*/ if (type_is_collatable(atttype))
{ if (!OidIsValid(attcollation))
ereport(ERROR,
(errcode(ERRCODE_INDETERMINATE_COLLATION),
errmsg("could not determine which collation to use for index expression"),
errhint("Use the COLLATE clause to set the collation explicitly.")));
} else
{ if (OidIsValid(attcollation))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("collations are not supported by type %s",
format_type_be(atttype))));
}
/* *Onlyallowcommutativeoperatorstobeusedinexclusion *constraints.IfXconflictswithY,butYdoesnotconflict *withX,badthingswillhappen.
*/ if (get_commutator(opid) != opid)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("operator %s is not commutative",
format_operator(opid)),
errdetail("Only commutative operators can be used in exclusion constraints.")));
/* *Operatormustbeamemberoftherightopfamily,too
*/
opfamily = get_opclass_family(opclassOids[attn]);
strat = get_op_opfamily_strategy(opid, opfamily); if (strat == 0)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("operator %s is not a member of operator family \"%s\"",
format_operator(opid),
get_opfamily_name(opfamily, false)),
errdetail("The exclusion operator must be related to the index operator class for the constraint.")));
/* *Setuptheper-columnoptions(indoptionfield).Fornow,thisis *zeroforanyun-orderedindex,whileorderedindexeshaveDESCand *NULLSFIRST/LASToptions.
*/
colOptions[attn] = 0; if (amcanorder)
{ /* default ordering is ASC */ if (attribute->ordering == SORTBY_DESC)
colOptions[attn] |= INDOPTION_DESC; /* default null ordering is LAST for ASC, FIRST for DESC */ if (attribute->nulls_ordering == SORTBY_NULLS_DEFAULT)
{ if (attribute->ordering == SORTBY_DESC)
colOptions[attn] |= INDOPTION_NULLS_FIRST;
} elseif (attribute->nulls_ordering == SORTBY_NULLS_FIRST)
colOptions[attn] |= INDOPTION_NULLS_FIRST;
} else
{ /* index AM does not support ordering */ if (attribute->ordering != SORTBY_DEFAULT)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("access method \"%s\" does not support ASC/DESC options",
accessMethodName))); if (attribute->nulls_ordering != SORTBY_NULLS_DEFAULT)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("access method \"%s\" does not support NULLS FIRST/LAST options",
accessMethodName)));
}
/* Set up the per-column opclass options (attoptions field). */ if (attribute->opclassopts)
{
Assert(attn < nkeycols);
/* *Resolvepossibly-defaultedoperatorclassspecification * *Note:Thisisusedtoresolveoperatorclassspecificationsinindexand *partitionkeydefinitions.
*/
Oid
ResolveOpClass(const List *opclass, Oid attrType, constchar *accessMethodName, Oid accessMethodId)
{ char *schemaname; char *opcname;
HeapTuple tuple;
Form_pg_opclass opform;
Oid opClassId,
opInputType;
if (opclass == NIL)
{ /* no operator class specified, so find the default */
opClassId = GetDefaultOpClass(attrType, accessMethodId); if (!OidIsValid(opClassId))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("data type %s has no default operator class for access method \"%s\"",
format_type_be(attrType), accessMethodName),
errhint("You must specify an operator class for the index or define a default operator class for the data type."))); return opClassId;
}
/* deconstruct the name list */
DeconstructQualifiedName(opclass, &schemaname, &opcname);
if (schemaname)
{ /* Look in specific schema only */
Oid namespaceId;
namespaceId = LookupExplicitNamespace(schemaname, false);
tuple = SearchSysCache3(CLAAMNAMENSP,
ObjectIdGetDatum(accessMethodId),
PointerGetDatum(opcname),
ObjectIdGetDatum(namespaceId));
} else
{ /* Unqualified opclass name, so search the search path */
opClassId = OpclassnameGetOpcid(accessMethodId, opcname); if (!OidIsValid(opClassId))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("operator class \"%s\" does not exist for access method \"%s\"",
opcname, accessMethodName)));
tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opClassId));
}
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("operator class \"%s\" does not exist for access method \"%s\"",
NameListToString(opclass), accessMethodName)));
if (!IsBinaryCoercible(attrType, opInputType))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("operator class \"%s\" does not accept data type %s",
NameListToString(opclass), format_type_be(attrType))));
ReleaseSysCache(tuple);
return opClassId;
}
/* *GetDefaultOpClass * *GiventheOIDsofadatatypeandanaccessmethod,findthedefault *operatorclass,ifany.ReturnsInvalidOidifthereisnone.
*/
Oid
GetDefaultOpClass(Oid type_id, Oid am_id)
{
Oid result = InvalidOid; int nexact = 0; int ncompatible = 0; int ncompatiblepreferred = 0;
Relation rel;
ScanKeyData skey[1];
SysScanDesc scan;
HeapTuple tup;
TYPCATEGORY tcategory;
/* If it's a domain, look at the base type instead */
type_id = getBaseType(type_id);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
/* ignore altogether if not a default opclass */ if (!opclass->opcdefault) continue; if (opclass->opcintype == type_id)
{
nexact++;
result = opclass->oid;
} elseif (nexact == 0 &&
IsBinaryCoercible(type_id, opclass->opcintype))
{ if (IsPreferredType(tcategory, opclass->opcintype))
{
ncompatiblepreferred++;
result = opclass->oid;
} elseif (ncompatiblepreferred == 0)
{
ncompatible++;
result = opclass->oid;
}
}
}
systable_endscan(scan);
table_close(rel, AccessShareLock);
/* raise error if pg_opclass contains inconsistent data */ if (nexact > 1)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("there are multiple default operator classes for data type %s",
format_type_be(type_id))));
if (get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
{ /* *AsktheindexAMtotranslatetoitsinternalstratnum
*/
*strat = IndexAmTranslateCompareType(cmptype, amid, opfamily, true); if (*strat == InvalidStrategy)
ereport(ERROR,
errcode(ERRCODE_UNDEFINED_OBJECT),
cmptype == COMPARE_EQ ? errmsg("could not identify an equality operator for type %s", format_type_be(opcintype)) :
cmptype == COMPARE_OVERLAP ? errmsg("could not identify an overlaps operator for type %s", format_type_be(opcintype)) :
cmptype == COMPARE_CONTAINED_BY ? errmsg("could not identify a contained-by operator for type %s", format_type_be(opcintype)) : 0,
errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
if (!OidIsValid(*opid))
ereport(ERROR,
errcode(ERRCODE_UNDEFINED_OBJECT),
cmptype == COMPARE_EQ ? errmsg("could not identify an equality operator for type %s", format_type_be(opcintype)) :
cmptype == COMPARE_OVERLAP ? errmsg("could not identify an overlaps operator for type %s", format_type_be(opcintype)) :
cmptype == COMPARE_CONTAINED_BY ? errmsg("could not identify a contained-by operator for type %s", format_type_be(opcintype)) : 0,
errdetail("There is no suitable operator in operator family \"%s\" for access method \"%s\".",
get_opfamily_name(opfamily, false), get_am_name(amid)));
}
/* *makeObjectName() * *Createanameforanimplicitlycreatedindex,sequence,constraint, *extendedstatistics,etc. * *Theparametersaretypically:theoriginaltablename,theoriginalfield *name,anda"type"string(suchas"seq"or"pkey").Thefieldname *and/ortypecanbeNULLifnotrelevant. * *Theresultisapalloc'dstring. * *Thebasicresultwewantis"name1_name2_label",omitting"_name2"or *"_label"whenthoseparametersareNULL.However,wemustgenerate *anamewithlessthanNAMEDATALENcharacters!So,wetruncateoneor *bothnamesifnecessarytomakeashort-enoughstring.Thelabelpart *isnevertruncated(soithadbetterbereasonablyshort). * *Thecallerisresponsibleforcheckinguniquenessofthegenerated *nameandretryingasneeded;retryingwillbedonebyalteringthe *"label"string(whichiswhywenevertruncatethatpart).
*/ char *
makeObjectName(constchar *name1, constchar *name2, constchar *label)
{ char *name; int overhead = 0; /* chars needed for label and underscores */ int availchars; /* chars available for name(s) */ int name1chars; /* chars allocated to name1 */ int name2chars; /* chars allocated to name2 */ int ndx;
name1chars = strlen(name1); if (name2)
{
name2chars = strlen(name2);
overhead++; /* allow for separating underscore */
} else
name2chars = 0; if (label)
overhead += strlen(label) + 1;
availchars = NAMEDATALEN - 1 - overhead;
Assert(availchars > 0); /* else caller chose a bad label */
/* *Ifwemusttruncate,preferentiallytruncatethelongername.This *logiccouldbeexpressedwithoutaloop,butit'ssimpleandobviousas *aloop.
*/ while (name1chars + name2chars > availchars)
{ if (name1chars > name2chars)
name1chars--; else
name2chars--;
}
/* Get the preliminary name from the IndexElem */ if (ielem->indexcolname)
origname = ielem->indexcolname; /* caller-specified name */ elseif (ielem->name)
origname = ielem->name; /* simple column reference */ else
origname = "expr"; /* default name for expression */
/* If it conflicts with any previous column, tweak it */
curname = origname; for (i = 1;; i++)
{
ListCell *lc2; char nbuf[32]; int nlen;
foreach(lc2, result)
{ if (strcmp(curname, (char *) lfirst(lc2)) == 0) break;
} if (lc2 == NULL) break; /* found nonconflicting name */
switch (stmt->kind)
{ case REINDEX_OBJECT_INDEX:
ReindexIndex(stmt, ¶ms, isTopLevel); break; case REINDEX_OBJECT_TABLE:
ReindexTable(stmt, ¶ms, isTopLevel); break; case REINDEX_OBJECT_SCHEMA: case REINDEX_OBJECT_SYSTEM: case REINDEX_OBJECT_DATABASE:
if (!result)
ereport(NOTICE,
(errmsg("table \"%s\" has no indexes that can be reindexed concurrently",
relation->relname)));
} else
{
ReindexParams newparams = *params;
newparams.options |= REINDEXOPT_REPORT_PROGRESS;
result = reindex_relation(stmt, heapOid,
REINDEX_REL_PROCESS_TOAST |
REINDEX_REL_CHECK_CONSTRAINTS,
&newparams); if (!result)
ereport(NOTICE,
(errmsg("table \"%s\" has no indexes to reindex",
relation->relname)));
}
if (objectName && strcmp(objectName, get_database_name(objectOid)) != 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("can only reindex the currently open database"))); if (!object_ownercheck(DatabaseRelationId, objectOid, GetUserId()) &&
!has_privs_of_role(GetUserId(), ROLE_PG_MAINTAIN))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_DATABASE,
get_database_name(objectOid));
}
/* Skip temp tables of other backends; we can't reindex them at all */ if (classtuple->relpersistence == RELPERSISTENCE_TEMP &&
!isTempNamespace(classtuple->relnamespace)) continue;
/* functions in indexes may want a snapshot set */
PushActiveSnapshot(GetTransactionSnapshot());
/* check if the relation still exists */ if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
{
PopActiveSnapshot();
CommitTransactionCommand(); continue;
}
if ((params->options & REINDEXOPT_VERBOSE) != 0)
{ /* Save data needed by REINDEX VERBOSE in private context */
oldcontext = MemoryContextSwitchTo(private_context);
/* *Extractthelistofindexesthataregoingtoberebuiltbasedonthe *relationOidgivenbycaller.
*/ switch (relkind)
{ case RELKIND_RELATION: case RELKIND_MATVIEW: case RELKIND_TOASTVALUE:
{ /* *Inthecaseofarelation,findallitsindexesincluding *toastindexes.
*/
Relation heapRelation;
/* Save the list of relation OIDs in private context */
oldcontext = MemoryContextSwitchTo(private_context);
/* Track this relation for session locks */
heapRelationIds = lappend_oid(heapRelationIds, relationOid);
MemoryContextSwitchTo(oldcontext);
if (IsCatalogRelationOid(relationOid))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot reindex system catalogs concurrently")));
/* Open relation to get its indexes */ if ((params->options & REINDEXOPT_MISSING_OK) != 0)
{
heapRelation = try_table_open(relationOid,
ShareUpdateExclusiveLock); /* leave if relation does not exist */ if (!heapRelation) break;
} else
heapRelation = table_open(relationOid,
ShareUpdateExclusiveLock);
if (OidIsValid(params->tablespaceOid) &&
IsSystemRelation(heapRelation))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot move system relation \"%s\"",
RelationGetRelationName(heapRelation))));
/* Add all the valid indexes of relation to list */
foreach(lc, RelationGetIndexList(heapRelation))
{
Oid cellOid = lfirst_oid(lc);
Relation indexRelation = index_open(cellOid,
ShareUpdateExclusiveLock);
if (!indexRelation->rd_index->indisvalid)
ereport(WARNING,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("skipping reindex of invalid index \"%s.%s\"",
get_namespace_name(get_rel_namespace(cellOid)),
get_rel_name(cellOid)),
errhint("Use DROP INDEX or REINDEX INDEX."))); elseif (indexRelation->rd_index->indisexclusion)
ereport(WARNING,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot reindex exclusion constraint index \"%s.%s\" concurrently, skipping",
get_namespace_name(get_rel_namespace(cellOid)),
get_rel_name(cellOid)))); else
{
ReindexIndexInfo *idx;
/* Save the list of relation OIDs in private context */
oldcontext = MemoryContextSwitchTo(private_context);
idx = palloc_object(ReindexIndexInfo);
idx->indexId = cellOid; /* other fields set later */
indexIds = lappend(indexIds, idx);
MemoryContextSwitchTo(oldcontext);
}
index_close(indexRelation, NoLock);
}
/* Also add the toast indexes */ if (OidIsValid(heapRelation->rd_rel->reltoastrelid))
{
Oid toastOid = heapRelation->rd_rel->reltoastrelid;
Relation toastRelation = table_open(toastOid,
ShareUpdateExclusiveLock);
/* Save the list of relation OIDs in private context */
oldcontext = MemoryContextSwitchTo(private_context);
/* Track this relation for session locks */
heapRelationIds = lappend_oid(heapRelationIds, toastOid);
if (!indexRelation->rd_index->indisvalid)
ereport(WARNING,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("skipping reindex of invalid index \"%s.%s\"",
get_namespace_name(get_rel_namespace(cellOid)),
get_rel_name(cellOid)),
errhint("Use DROP INDEX or REINDEX INDEX."))); else
{
ReindexIndexInfo *idx;
idx = palloc_object(ReindexIndexInfo);
idx->indexId = cellOid;
indexIds = lappend(indexIds, idx); /* other fields set later */
MemoryContextSwitchTo(oldcontext);
}
index_close(indexRelation, NoLock);
}
table_close(toastRelation, NoLock);
}
table_close(heapRelation, NoLock); break;
} case RELKIND_INDEX:
{
Oid heapId = IndexGetRelation(relationOid,
(params->options & REINDEXOPT_MISSING_OK) != 0);
Relation heapRelation;
ReindexIndexInfo *idx;
/* if relation is missing, leave */ if (!OidIsValid(heapId)) break;
if (IsCatalogRelationOid(heapId))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot reindex system catalogs concurrently")));
/* *Don'tallowreindexforaninvalidindexonTOASTtable,as *ifrebuiltitwouldnotbepossibletodropit.Match *errormessageinreindex_index().
*/ if (IsToastNamespace(get_rel_namespace(relationOid)) &&
!get_index_isvalid(relationOid))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot reindex invalid index on TOAST table")));
/* *Checkifparentrelationcanbelockedandifitexists, *thisneedstobedoneatthisstageasthelistofindexes *torebuildisnotcompleteyet,andREINDEXOPT_MISSING_OK *shouldnotbeusedonceallthesessionlocksaretaken.
*/ if ((params->options & REINDEXOPT_MISSING_OK) != 0)
{
heapRelation = try_table_open(heapId,
ShareUpdateExclusiveLock); /* leave if relation does not exist */ if (!heapRelation) break;
} else
heapRelation = table_open(heapId,
ShareUpdateExclusiveLock);
if (OidIsValid(params->tablespaceOid) &&
IsSystemRelation(heapRelation))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot move system relation \"%s\"",
get_rel_name(relationOid))));
table_close(heapRelation, NoLock);
/* Save the list of relation OIDs in private context */
oldcontext = MemoryContextSwitchTo(private_context);
/* Track the heap relation of this index for session locks */
heapRelationIds = list_make1_oid(heapId);
/* *SavethelistofrelationOIDsinprivatecontext.Note *thatinvalidindexesareallowedhere.
*/
idx = palloc_object(ReindexIndexInfo);
idx->indexId = relationOid;
indexIds = lappend(indexIds, idx); /* other fields set later */
MemoryContextSwitchTo(oldcontext); break;
}
case RELKIND_PARTITIONED_TABLE: case RELKIND_PARTITIONED_INDEX: default: /* Return error if type of relation is not supported */
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot reindex this type of relation concurrently"))); break;
}
/* It's not a shared catalog, so refuse to move it to shared tablespace */ if (params->tablespaceOid == GLOBALTABLESPACE_OID)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot move non-shared relation to tablespace \"%s\"",
get_tablespace_name(params->tablespaceOid))));
/* determine safety of this index for set_indexsafe_procflags */
idx->safe = (RelationGetIndexExpressions(indexRel) == NIL &&
RelationGetIndexPredicate(indexRel) == NIL);
#ifdef USE_INJECTION_POINTS if (idx->safe)
INJECTION_POINT("reindex-conc-index-safe", NULL); else
INJECTION_POINT("reindex-conc-index-not-safe", NULL); #endif
/* This function shouldn't be called for temporary relations. */ if (indexRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
elog(ERROR, "cannot reindex a temporary table concurrently");
/* Choose a temporary relation name for the new index */
concurrentName = ChooseRelationName(get_rel_name(idx->indexId),
NULL, "ccnew",
get_rel_namespace(indexRel->rd_index->indrelid), false);
/* Choose the new tablespace, indexes of toast tables are not moved */ if (OidIsValid(params->tablespaceOid) &&
heapRel->rd_rel->relkind != RELKIND_TOASTVALUE)
tablespaceid = params->tablespaceOid; else
tablespaceid = indexRel->rd_rel->reltablespace;
/* Create new index definition based on given index */
newIndexId = index_concurrently_create_copy(heapRel,
idx->indexId,
tablespaceid,
concurrentName);
/* Save the list of locks in private context */
oldcontext = MemoryContextSwitchTo(private_context);
/* Add lockrelid of heap relation to the list of locked relations */
lockrelid = palloc_object(LockRelId);
*lockrelid = heapRelation->rd_lockInfo.lockRelId;
relationLocks = lappend(relationLocks, lockrelid);
heaplocktag = palloc_object(LOCKTAG);
/* Save the LOCKTAG for this parent relation for the wait phase */
SET_LOCKTAG_RELATION(*heaplocktag, lockrelid->dbId, lockrelid->relId);
lockTags = lappend(lockTags, heaplocktag);
MemoryContextSwitchTo(oldcontext);
/* Close heap relation */
table_close(heapRelation, NoLock);
}
/* Get a session-level lock on each table. */
foreach(lc, relationLocks)
{
LockRelId *lockrelid = (LockRelId *) lfirst(lc);
/* Choose a relation name for old index */
oldName = ChooseRelationName(get_rel_name(oldidx->indexId),
NULL, "ccold",
get_rel_namespace(oldidx->tableId), false);
/* Start a new transaction to finish process properly */
StartTransactionCommand();
/* Log what we did */ if ((params->options & REINDEXOPT_VERBOSE) != 0)
{ if (relkind == RELKIND_INDEX)
ereport(INFO,
(errmsg("index \"%s.%s\" was reindexed",
relationNamespace, relationName),
errdetail("%s.",
pg_rusage_show(&ru0)))); else
{
foreach(lc, newIndexIds)
{
ReindexIndexInfo *idx = lfirst(lc);
Oid indOid = idx->indexId;
ereport(INFO,
(errmsg("index \"%s.%s\" was reindexed",
get_namespace_name(get_rel_namespace(indOid)),
get_rel_name(indOid)))); /* Don't show rusage here, since it's not per index. */
}
if (parentOid == InvalidOid)
{ /* *Thereexistsapg_inheritsrow,whichwewanttoclear;doso.
*/
CatalogTupleDelete(pg_inherits, &tuple->t_self);
fix_dependencies = true;
} else
{ /* *Apg_inheritsrowexists.Ifit'sthesamewewant,thenwe're *good;ifitdiffers,thatamountstoacorruptcatalogand *shouldnothappen.
*/ if (inhForm->inhparent != parentOid)
{ /* unexpected: we should not get called in this case */
elog(ERROR, "bogus pg_inherit row: inhrelid %u inhparent %u",
inhForm->inhrelid, inhForm->inhparent);
}
/* already in the right state */
fix_dependencies = false;
}
}
/* done with pg_inherits */
systable_endscan(scan);
relation_close(pg_inherits, RowExclusiveLock);
/* set relhassubclass if an index partition has been added to the parent */ if (OidIsValid(parentOid))
{
LockRelationOid(parentOid, ShareUpdateExclusiveLock);
SetRelationHasSubclass(parentOid, true);
}
/* set relispartition correctly on the partition */
update_relispartition(partRelid, OidIsValid(parentOid));
if (fix_dependencies)
{ /* *Insert/deletepg_dependrows.Ifsettingaparent,addPARTITION *dependenciesontheparentindexandthetable;ifremovinga *parent,deletePARTITIONdependencies.
*/ if (OidIsValid(parentOid))
{
ObjectAddress partIdx;
ObjectAddress parentIdx;
ObjectAddress partitionTbl;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.