mirror of
https://github.com/postgres/postgres.git
synced 2025-11-22 12:22:45 +03:00
Fix handling of expressions and predicates in REINDEX CONCURRENTLY
When copying the definition of an index rebuilt concurrently for the new entry, the index information was taken directly from the old index using the relation cache. In this case, predicates and expressions have some post-processing to prepare things for the planner, which loses some information including the collations added in any of them. This inconsistency can cause issues when attempting for example a table rewrite, and makes the new indexes rebuilt concurrently inconsistent with the old entries. In order to fix the problem, fetch expressions and predicates directly from the catalog of the old entry, and fill in IndexInfo for the new index with that. This makes the process more consistent with DefineIndex(), and the code is refactored with the addition of a routine to create an IndexInfo node. Reported-by: Manuel Rigger Author: Michael Paquier Discussion: https://postgr.es/m/CA+u7OA5Hp0ra235F3czPom_FyAd-3+XwSJmX95r1+sRPOJc9VQ@mail.gmail.com Backpatch-through: 12
This commit is contained in:
@@ -1197,7 +1197,8 @@ Oid
|
||||
index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char *newName)
|
||||
{
|
||||
Relation indexRelation;
|
||||
IndexInfo *indexInfo;
|
||||
IndexInfo *oldInfo,
|
||||
*newInfo;
|
||||
Oid newIndexId = InvalidOid;
|
||||
HeapTuple indexTuple,
|
||||
classTuple;
|
||||
@@ -1208,11 +1209,22 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char
|
||||
int2vector *indcoloptions;
|
||||
bool isnull;
|
||||
List *indexColNames = NIL;
|
||||
List *indexExprs = NIL;
|
||||
List *indexPreds = NIL;
|
||||
|
||||
indexRelation = index_open(oldIndexId, RowExclusiveLock);
|
||||
|
||||
/* New index uses the same index information as old index */
|
||||
indexInfo = BuildIndexInfo(indexRelation);
|
||||
/* The new index needs some information from the old index */
|
||||
oldInfo = BuildIndexInfo(indexRelation);
|
||||
|
||||
/*
|
||||
* Concurrent build of an index with exclusion constraints is not
|
||||
* supported.
|
||||
*/
|
||||
if (oldInfo->ii_ExclusionOps != NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("concurrent index creation for exclusion constraints is not supported")));
|
||||
|
||||
/* Get the array of class and column options IDs from index info */
|
||||
indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(oldIndexId));
|
||||
@@ -1236,14 +1248,64 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char
|
||||
Anum_pg_class_reloptions, &isnull);
|
||||
|
||||
/*
|
||||
* Extract the list of column names to be used for the index creation.
|
||||
* Fetch the list of expressions and predicates directly from the
|
||||
* catalogs. This cannot rely on the information from IndexInfo of the
|
||||
* old index as these have been flattened for the planner.
|
||||
*/
|
||||
for (int i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
|
||||
if (oldInfo->ii_Expressions != NIL)
|
||||
{
|
||||
Datum exprDatum;
|
||||
char *exprString;
|
||||
|
||||
exprDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
|
||||
Anum_pg_index_indexprs, &isnull);
|
||||
Assert(!isnull);
|
||||
exprString = TextDatumGetCString(exprDatum);
|
||||
indexExprs = (List *) stringToNode(exprString);
|
||||
pfree(exprString);
|
||||
}
|
||||
if (oldInfo->ii_Predicate != NIL)
|
||||
{
|
||||
Datum predDatum;
|
||||
char *predString;
|
||||
|
||||
predDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
|
||||
Anum_pg_index_indpred, &isnull);
|
||||
Assert(!isnull);
|
||||
predString = TextDatumGetCString(predDatum);
|
||||
indexPreds = (List *) stringToNode(predString);
|
||||
|
||||
/* Also convert to implicit-AND format */
|
||||
indexPreds = make_ands_implicit((Expr *) indexPreds);
|
||||
pfree(predString);
|
||||
}
|
||||
|
||||
/*
|
||||
* Build the index information for the new index. Note that rebuild of
|
||||
* indexes with exclusion constraints is not supported, hence there is no
|
||||
* need to fill all the ii_Exclusion* fields.
|
||||
*/
|
||||
newInfo = makeIndexInfo(oldInfo->ii_NumIndexAttrs,
|
||||
oldInfo->ii_NumIndexKeyAttrs,
|
||||
oldInfo->ii_Am,
|
||||
indexExprs,
|
||||
indexPreds,
|
||||
oldInfo->ii_Unique,
|
||||
false, /* not ready for inserts */
|
||||
true);
|
||||
|
||||
/*
|
||||
* Extract the list of column names and the column numbers for the new
|
||||
* index information. All this information will be used for the index
|
||||
* creation.
|
||||
*/
|
||||
for (int i = 0; i < oldInfo->ii_NumIndexAttrs; i++)
|
||||
{
|
||||
TupleDesc indexTupDesc = RelationGetDescr(indexRelation);
|
||||
Form_pg_attribute att = TupleDescAttr(indexTupDesc, i);
|
||||
|
||||
indexColNames = lappend(indexColNames, NameStr(att->attname));
|
||||
newInfo->ii_IndexAttrNumbers[i] = oldInfo->ii_IndexAttrNumbers[i];
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1259,7 +1321,7 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char
|
||||
InvalidOid, /* parentIndexRelid */
|
||||
InvalidOid, /* parentConstraintId */
|
||||
InvalidOid, /* relFileNode */
|
||||
indexInfo,
|
||||
newInfo,
|
||||
indexColNames,
|
||||
indexRelation->rd_rel->relam,
|
||||
indexRelation->rd_rel->reltablespace,
|
||||
|
||||
Reference in New Issue
Block a user