And here is a version for 9.1. This omits the code changes directly
relevant to DROP INDEX CONCURRENTLY, but includes the changes to avoid
transactional updates of the pg_index row during CREATE CONCURRENTLY,
as well as the changes to prevent use of not-valid or not-ready indexes
in places where it matters. I also chose to keep on using the
IndexIsValid and IndexIsReady macros, so as to avoid unnecessary
divergences of the branches.
I think this much of the patch needs to go into all supported branches.
regards, tom lane
diff --git a/src/backend/access/heap/README.HOT b/src/backend/access/heap/README.HOT
index f12cad44e56c363e62fa617497bbedfe1ba8c1fe..7cbc6a1d7e7328364938ef5d69bebe865524d7f8 100644
*** a/src/backend/access/heap/README.HOT
--- b/src/backend/access/heap/README.HOT
*************** from the index, as well as ensuring that
*** 386,391 ****
--- 386,397 ----
rows in a broken HOT chain (the first condition is stronger than the
second). Finally, we can mark the index valid for searches.
+ Note that we do not need to set pg_index.indcheckxmin in this code path,
+ because we have outwaited any transactions that would need to avoid using
+ the index. (indcheckxmin is only needed because non-concurrent CREATE
+ INDEX doesn't want to wait; its stronger lock would create too much risk of
+ deadlock if it did.)
+
Limitations and Restrictions
----------------------------
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 2e023d5ab56ac4efe1ab243739b48149e67a7408..129a1ac11f0e79408961abd73f0a33395569c5d5 100644
*** a/src/backend/catalog/index.c
--- b/src/backend/catalog/index.c
*************** static void ResetReindexPending(void);
*** 129,134 ****
--- 129,138 ----
* See whether an existing relation has a primary key.
*
* Caller must have suitable lock on the relation.
+ *
+ * Note: we intentionally do not check IndexIsValid here; that's because this
+ * is used to enforce the rule that there can be only one indisprimary index,
+ * and we want that to be true even if said index is invalid.
*/
static bool
relationHasPrimaryKey(Relation rel)
*************** index_constraint_create(Relation heapRel
*** 1247,1254 ****
* Note: since this is a transactional update, it's unsafe against
* concurrent SnapshotNow scans of pg_index. When making an existing
* index into a constraint, caller must have a table lock that prevents
! * concurrent table updates, and there is a risk that concurrent readers
! * of the table will miss seeing this index at all.
*/
if (update_pgindex && (mark_as_primary || deferrable))
{
--- 1251,1259 ----
* Note: since this is a transactional update, it's unsafe against
* concurrent SnapshotNow scans of pg_index. When making an existing
* index into a constraint, caller must have a table lock that prevents
! * concurrent table updates; if it's less than a full exclusive lock,
! * there is a risk that concurrent readers of the table will miss seeing
! * this index at all.
*/
if (update_pgindex && (mark_as_primary || deferrable))
{
*************** BuildIndexInfo(Relation index)
*** 1450,1456 ****
/* other info */
ii->ii_Unique = indexStruct->indisunique;
! ii->ii_ReadyForInserts = indexStruct->indisready;
/* initialize index-build state to default */
ii->ii_Concurrent = false;
--- 1455,1461 ----
/* other info */
ii->ii_Unique = indexStruct->indisunique;
! ii->ii_ReadyForInserts = IndexIsReady(indexStruct);
/* initialize index-build state to default */
ii->ii_Concurrent = false;
*************** index_build(Relation heapRelation,
*** 1789,1796 ****
* index's usability horizon. Moreover, we *must not* try to change the
* index's pg_index entry while reindexing pg_index itself, and this
* optimization nicely prevents that.
*/
! if (indexInfo->ii_BrokenHotChain && !isreindex)
{
Oid indexId = RelationGetRelid(indexRelation);
Relation pg_index;
--- 1794,1813 ----
* index's usability horizon. Moreover, we *must not* try to change the
* index's pg_index entry while reindexing pg_index itself, and this
* optimization nicely prevents that.
+ *
+ * We also need not set indcheckxmin during a concurrent index build,
+ * because we won't set indisvalid true until all transactions that care
+ * about the broken HOT chains are gone.
+ *
+ * Therefore, this code path can only be taken during non-concurrent
+ * CREATE INDEX. Thus the fact that heap_update will set the pg_index
+ * tuple's xmin doesn't matter, because that tuple was created in the
+ * current transaction anyway. That also means we don't need to worry
+ * about any concurrent readers of the tuple; no other transaction can see
+ * it yet.
*/
! if (indexInfo->ii_BrokenHotChain && !isreindex &&
! !indexInfo->ii_Concurrent)
{
Oid indexId = RelationGetRelid(indexRelation);
Relation pg_index;
*************** validate_index_heapscan(Relation heapRel
*** 2754,2759 ****
--- 2771,2835 ----
/*
+ * index_set_state_flags - adjust pg_index state flags
+ *
+ * This is used during CREATE INDEX CONCURRENTLY to adjust the pg_index
+ * flags that denote the index's state. We must use an in-place update of
+ * the pg_index tuple, because we do not have exclusive lock on the parent
+ * table and so other sessions might concurrently be doing SnapshotNow scans
+ * of pg_index to identify the table's indexes. A transactional update would
+ * risk somebody not seeing the index at all. Because the update is not
+ * transactional and will not roll back on error, this must only be used as
+ * the last step in a transaction that has not made any transactional catalog
+ * updates!
+ *
+ * Note that heap_inplace_update does send a cache inval message for the
+ * tuple, so other sessions will hear about the update as soon as we commit.
+ */
+ void
+ index_set_state_flags(Oid indexId, IndexStateFlagsAction action)
+ {
+ Relation pg_index;
+ HeapTuple indexTuple;
+ Form_pg_index indexForm;
+
+ /* Assert that current xact hasn't done any transactional updates */
+ Assert(GetTopTransactionIdIfAny() == InvalidTransactionId);
+
+ /* Open pg_index and fetch a writable copy of the index's tuple */
+ pg_index = heap_open(IndexRelationId, RowExclusiveLock);
+
+ indexTuple = SearchSysCacheCopy1(INDEXRELID,
+ ObjectIdGetDatum(indexId));
+ if (!HeapTupleIsValid(indexTuple))
+ elog(ERROR, "cache lookup failed for index %u", indexId);
+ indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
+
+ /* Perform the requested state change on the copy */
+ switch (action)
+ {
+ case INDEX_CREATE_SET_READY:
+ /* Set indisready during a CREATE INDEX CONCURRENTLY sequence */
+ Assert(!indexForm->indisready);
+ Assert(!indexForm->indisvalid);
+ indexForm->indisready = true;
+ break;
+ case INDEX_CREATE_SET_VALID:
+ /* Set indisvalid during a CREATE INDEX CONCURRENTLY sequence */
+ Assert(indexForm->indisready);
+ Assert(!indexForm->indisvalid);
+ indexForm->indisvalid = true;
+ break;
+ }
+
+ /* ... and write it back in-place */
+ heap_inplace_update(pg_index, indexTuple);
+
+ heap_close(pg_index, RowExclusiveLock);
+ }
+
+
+ /*
* IndexGetRelation: given an index's relation OID, get the OID of the
* relation it is an index on. Uses the system cache.
*/
*************** void
*** 2782,2793 ****
reindex_index(Oid indexId, bool skip_constraint_checks)
{
Relation iRel,
! heapRelation,
! pg_index;
Oid heapId;
IndexInfo *indexInfo;
- HeapTuple indexTuple;
- Form_pg_index indexForm;
volatile bool skipped_constraint = false;
/*
--- 2858,2866 ----
reindex_index(Oid indexId, bool skip_constraint_checks)
{
Relation iRel,
! heapRelation;
Oid heapId;
IndexInfo *indexInfo;
volatile bool skipped_constraint = false;
/*
*************** reindex_index(Oid indexId, bool skip_con
*** 2867,2891 ****
*
* We can also reset indcheckxmin, because we have now done a
* non-concurrent index build, *except* in the case where index_build
! * found some still-broken HOT chains. If it did, we normally leave
! * indcheckxmin alone (note that index_build won't have changed it,
! * because this is a reindex). But if the index was invalid or not ready
! * and there were broken HOT chains, it seems best to force indcheckxmin
! * true, because the normal argument that the HOT chains couldn't conflict
! * with the index is suspect for an invalid index.
*
! * Note that it is important to not update the pg_index entry if we don't
! * have to, because updating it will move the index's usability horizon
! * (recorded as the tuple's xmin value) if indcheckxmin is true. We don't
! * really want REINDEX to move the usability horizon forward ever, but we
! * have no choice if we are to fix indisvalid or indisready. Of course,
! * clearing indcheckxmin eliminates the issue, so we're happy to do that
! * if we can. Another reason for caution here is that while reindexing
! * pg_index itself, we must not try to update it. We assume that
! * pg_index's indexes will always have these flags in their clean state.
*/
if (!skipped_constraint)
{
pg_index = heap_open(IndexRelationId, RowExclusiveLock);
indexTuple = SearchSysCacheCopy1(INDEXRELID,
--- 2940,2978 ----
*
* We can also reset indcheckxmin, because we have now done a
* non-concurrent index build, *except* in the case where index_build
! * found some still-broken HOT chains. If it did, and we don't have to
! * change any of the other flags, we just leave indcheckxmin alone (note
! * that index_build won't have changed it, because this is a reindex).
! * This is okay and desirable because not updating the tuple leaves the
! * index's usability horizon (recorded as the tuple's xmin value) the same
! * as it was.
*
! * But, if the index was invalid/not-ready and there were broken HOT
! * chains, we had better force indcheckxmin true, because the normal
! * argument that the HOT chains couldn't conflict with the index is
! * suspect for an invalid index. In this case advancing the usability
! * horizon is appropriate.
! *
! * Note that if we have to update the tuple, there is a risk of concurrent
! * transactions not seeing it during their SnapshotNow scans of pg_index.
! * While not especially desirable, this is safe because no such
! * transaction could be trying to update the table (since we have
! * ShareLock on it). The worst case is that someone might transiently
! * fail to use the index for a query --- but it was probably unusable
! * before anyway, if we are updating the tuple.
! *
! * Another reason for avoiding unnecessary updates here is that while
! * reindexing pg_index itself, we must not try to update tuples in it.
! * pg_index's indexes should always have these flags in their clean state,
! * so that won't happen.
*/
if (!skipped_constraint)
{
+ Relation pg_index;
+ HeapTuple indexTuple;
+ Form_pg_index indexForm;
+ bool index_bad;
+
pg_index = heap_open(IndexRelationId, RowExclusiveLock);
indexTuple = SearchSysCacheCopy1(INDEXRELID,
*************** reindex_index(Oid indexId, bool skip_con
*** 2894,2910 ****
elog(ERROR, "cache lookup failed for index %u", indexId);
indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
! if (!indexForm->indisvalid || !indexForm->indisready ||
(indexForm->indcheckxmin && !indexInfo->ii_BrokenHotChain))
{
if (!indexInfo->ii_BrokenHotChain)
indexForm->indcheckxmin = false;
! else if (!indexForm->indisvalid || !indexForm->indisready)
indexForm->indcheckxmin = true;
indexForm->indisvalid = true;
indexForm->indisready = true;
simple_heap_update(pg_index, &indexTuple->t_self, indexTuple);
CatalogUpdateIndexes(pg_index, indexTuple);
}
heap_close(pg_index, RowExclusiveLock);
--- 2981,3008 ----
elog(ERROR, "cache lookup failed for index %u", indexId);
indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
! index_bad = (!indexForm->indisvalid ||
! !indexForm->indisready);
! if (index_bad ||
(indexForm->indcheckxmin && !indexInfo->ii_BrokenHotChain))
{
if (!indexInfo->ii_BrokenHotChain)
indexForm->indcheckxmin = false;
! else if (index_bad)
indexForm->indcheckxmin = true;
indexForm->indisvalid = true;
indexForm->indisready = true;
simple_heap_update(pg_index, &indexTuple->t_self, indexTuple);
CatalogUpdateIndexes(pg_index, indexTuple);
+
+ /*
+ * Invalidate the relcache for the table, so that after we commit
+ * all sessions will refresh the table's index list. This ensures
+ * that if anyone misses seeing the pg_index row during this
+ * update, they'll refresh their list before attempting any update
+ * on the table.
+ */
+ CacheInvalidateRelcache(heapRelation);
}
heap_close(pg_index, RowExclusiveLock);
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 5dec4298e2c735438861739ed0b3adc87e1eea6d..81e72825269220e66488c93fef7b26411fe58f9b 100644
*** a/src/backend/commands/cluster.c
--- b/src/backend/commands/cluster.c
*************** check_index_is_clusterable(Relation OldH
*** 454,460 ****
* might put recently-dead tuples out-of-order in the new table, and there
* is little harm in that.)
*/
! if (!OldIndex->rd_index->indisvalid)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot cluster on invalid index \"%s\"",
--- 454,460 ----
* might put recently-dead tuples out-of-order in the new table, and there
* is little harm in that.)
*/
! if (!IndexIsValid(OldIndex->rd_index))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot cluster on invalid index \"%s\"",
*************** check_index_is_clusterable(Relation OldH
*** 468,473 ****
--- 468,478 ----
* mark_index_clustered: mark the specified index as the one clustered on
*
* With indexOid == InvalidOid, will mark all indexes of rel not-clustered.
+ *
+ * Note: we do transactional updates of the pg_index rows, which are unsafe
+ * against concurrent SnapshotNow scans of pg_index. Therefore this is unsafe
+ * to execute with less than full exclusive lock on the parent table;
+ * otherwise concurrent executions of RelationGetIndexList could miss indexes.
*/
void
mark_index_clustered(Relation rel, Oid indexOid)
*************** mark_index_clustered(Relation rel, Oid i
*** 523,528 ****
--- 528,536 ----
}
else if (thisIndexOid == indexOid)
{
+ /* this was checked earlier, but let's be real sure */
+ if (!IndexIsValid(indexForm))
+ elog(ERROR, "cannot cluster on invalid index %u", indexOid);
indexForm->indisclustered = true;
simple_heap_update(pg_index, &indexTuple->t_self, indexTuple);
CatalogUpdateIndexes(pg_index, indexTuple);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index b7c021d943a99e96ff5aab93caff7aecc83df66f..a08ad1324ba34e9c06d91cad50c4beb3d345892e 100644
*** a/src/backend/commands/indexcmds.c
--- b/src/backend/commands/indexcmds.c
*************** DefineIndex(RangeVar *heapRelation,
*** 148,156 ****
LockRelId heaprelid;
LOCKTAG heaplocktag;
Snapshot snapshot;
- Relation pg_index;
- HeapTuple indexTuple;
- Form_pg_index indexForm;
int i;
/*
--- 148,153 ----
*************** DefineIndex(RangeVar *heapRelation,
*** 531,553 ****
* commit this transaction, any new transactions that open the table must
* insert new entries into the index for insertions and non-HOT updates.
*/
! pg_index = heap_open(IndexRelationId, RowExclusiveLock);
!
! indexTuple = SearchSysCacheCopy1(INDEXRELID,
! ObjectIdGetDatum(indexRelationId));
! if (!HeapTupleIsValid(indexTuple))
! elog(ERROR, "cache lookup failed for index %u", indexRelationId);
! indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
!
! Assert(!indexForm->indisready);
! Assert(!indexForm->indisvalid);
!
! indexForm->indisready = true;
!
! simple_heap_update(pg_index, &indexTuple->t_self, indexTuple);
! CatalogUpdateIndexes(pg_index, indexTuple);
!
! heap_close(pg_index, RowExclusiveLock);
/* we can do away with our snapshot */
PopActiveSnapshot();
--- 528,534 ----
* commit this transaction, any new transactions that open the table must
* insert new entries into the index for insertions and non-HOT updates.
*/
! index_set_state_flags(indexRelationId, INDEX_CREATE_SET_READY);
/* we can do away with our snapshot */
PopActiveSnapshot();
*************** DefineIndex(RangeVar *heapRelation,
*** 671,693 ****
/*
* Index can now be marked valid -- update its pg_index entry
*/
! pg_index = heap_open(IndexRelationId, RowExclusiveLock);
!
! indexTuple = SearchSysCacheCopy1(INDEXRELID,
! ObjectIdGetDatum(indexRelationId));
! if (!HeapTupleIsValid(indexTuple))
! elog(ERROR, "cache lookup failed for index %u", indexRelationId);
! indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
!
! Assert(indexForm->indisready);
! Assert(!indexForm->indisvalid);
!
! indexForm->indisvalid = true;
!
! simple_heap_update(pg_index, &indexTuple->t_self, indexTuple);
! CatalogUpdateIndexes(pg_index, indexTuple);
!
! heap_close(pg_index, RowExclusiveLock);
/*
* The pg_index update will cause backends (including this one) to update
--- 652,658 ----
/*
* Index can now be marked valid -- update its pg_index entry
*/
! index_set_state_flags(indexRelationId, INDEX_CREATE_SET_VALID);
/*
* The pg_index update will cause backends (including this one) to update
*************** DefineIndex(RangeVar *heapRelation,
*** 695,701 ****
* relcache inval on the parent table to force replanning of cached plans.
* Otherwise existing sessions might fail to use the new index where it
* would be useful. (Note that our earlier commits did not create reasons
! * to replan; relcache flush on the index itself was sufficient.)
*/
CacheInvalidateRelcacheByRelid(heaprelid.relId);
--- 660,666 ----
* relcache inval on the parent table to force replanning of cached plans.
* Otherwise existing sessions might fail to use the new index where it
* would be useful. (Note that our earlier commits did not create reasons
! * to replan; so relcache flush on the index itself was sufficient.)
*/
CacheInvalidateRelcacheByRelid(heaprelid.relId);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 99fb892c1941955d1ae6acc15d68a5a5a50ad641..d7bb20fa2ec17aba06b61b24d31185fd06eecc0f 100644
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
*************** ATExecDropNotNull(Relation rel, const ch
*** 4528,4533 ****
--- 4528,4535 ----
/*
* Check that the attribute is not in a primary key
+ *
+ * Note: we'll throw error even if the pkey index is not valid.
*/
/* Loop over all indexes on the relation */
*************** transformFkeyGetPrimaryKey(Relation pkre
*** 5876,5882 ****
/*
* Get the list of index OIDs for the table from the relcache, and look up
* each one in the pg_index syscache until we find one marked primary key
! * (hopefully there isn't more than one such).
*/
*indexOid = InvalidOid;
--- 5878,5884 ----
/*
* Get the list of index OIDs for the table from the relcache, and look up
* each one in the pg_index syscache until we find one marked primary key
! * (hopefully there isn't more than one such). Insist it's valid, too.
*/
*indexOid = InvalidOid;
*************** transformFkeyGetPrimaryKey(Relation pkre
*** 5890,5896 ****
if (!HeapTupleIsValid(indexTuple))
elog(ERROR, "cache lookup failed for index %u", indexoid);
indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
! if (indexStruct->indisprimary)
{
/*
* Refuse to use a deferrable primary key. This is per SQL spec,
--- 5892,5898 ----
if (!HeapTupleIsValid(indexTuple))
elog(ERROR, "cache lookup failed for index %u", indexoid);
indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
! if (indexStruct->indisprimary && IndexIsValid(indexStruct))
{
/*
* Refuse to use a deferrable primary key. This is per SQL spec,
*************** transformFkeyCheckAttrs(Relation pkrel,
*** 5988,5997 ****
/*
* Must have the right number of columns; must be unique and not a
! * partial index; forget it if there are any expressions, too
*/
if (indexStruct->indnatts == numattrs &&
indexStruct->indisunique &&
heap_attisnull(indexTuple, Anum_pg_index_indpred) &&
heap_attisnull(indexTuple, Anum_pg_index_indexprs))
{
--- 5990,6001 ----
/*
* Must have the right number of columns; must be unique and not a
! * partial index; forget it if there are any expressions, too. Invalid
! * indexes are out as well.
*/
if (indexStruct->indnatts == numattrs &&
indexStruct->indisunique &&
+ IndexIsValid(indexStruct) &&
heap_attisnull(indexTuple, Anum_pg_index_indpred) &&
heap_attisnull(indexTuple, Anum_pg_index_indexprs))
{
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index fdd9fb4336ec52828adb5e92c17a5fa10aaa2652..bfdf8e38b1af6edb03e64c4dc50fb7e1638e8bf0 100644
*** a/src/backend/commands/vacuum.c
--- b/src/backend/commands/vacuum.c
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1078,1086 ****
/*
! * Open all the indexes of the given relation, obtaining the specified kind
! * of lock on each. Return an array of Relation pointers for the indexes
! * into *Irel, and the number of indexes into *nindexes.
*/
void
vac_open_indexes(Relation relation, LOCKMODE lockmode,
--- 1078,1093 ----
/*
! * Open all the vacuumable indexes of the given relation, obtaining the
! * specified kind of lock on each. Return an array of Relation pointers for
! * the indexes into *Irel, and the number of indexes into *nindexes.
! *
! * We consider an index vacuumable if it is marked insertable (IndexIsReady).
! * If it isn't, probably a CREATE INDEX CONCURRENTLY command failed early in
! * execution, and what we have is too corrupt to be processable. We will
! * vacuum even if the index isn't indisvalid; this is important because in a
! * unique index, uniqueness checks will be performed anyway and had better not
! * hit dangling index pointers.
*/
void
vac_open_indexes(Relation relation, LOCKMODE lockmode,
*************** vac_open_indexes(Relation relation, LOCK
*** 1094,1114 ****
indexoidlist = RelationGetIndexList(relation);
! *nindexes = list_length(indexoidlist);
! if (*nindexes > 0)
! *Irel = (Relation *) palloc(*nindexes * sizeof(Relation));
else
*Irel = NULL;
i = 0;
foreach(indexoidscan, indexoidlist)
{
Oid indexoid = lfirst_oid(indexoidscan);
! (*Irel)[i++] = index_open(indexoid, lockmode);
}
list_free(indexoidlist);
}
--- 1101,1130 ----
indexoidlist = RelationGetIndexList(relation);
! /* allocate enough memory for all indexes */
! i = list_length(indexoidlist);
! if (i > 0)
! *Irel = (Relation *) palloc(i * sizeof(Relation));
else
*Irel = NULL;
+ /* collect just the ready indexes */
i = 0;
foreach(indexoidscan, indexoidlist)
{
Oid indexoid = lfirst_oid(indexoidscan);
+ Relation indrel;
! indrel = index_open(indexoid, lockmode);
! if (IndexIsReady(indrel->rd_index))
! (*Irel)[i++] = indrel;
! else
! index_close(indrel, lockmode);
}
+ *nindexes = i;
+
list_free(indexoidlist);
}
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 9f959c539e880ad90704b74205d2f1d91cd6d2f4..bfacc6c30acf6c16565a6b711590ec22969ecfb0 100644
*** a/src/backend/executor/execUtils.c
--- b/src/backend/executor/execUtils.c
*************** ExecOpenIndices(ResultRelInfo *resultRel
*** 907,912 ****
--- 907,915 ----
/*
* For each index, open the index relation and save pg_index info. We
* acquire RowExclusiveLock, signifying we will update the index.
+ *
+ * Note: we do this even if the index is not IndexIsReady; it's not worth
+ * the trouble to optimize for the case where it isn't.
*/
i = 0;
foreach(l, indexoidlist)
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 77e701d2fc932d5bbfabccab3b552d038828fbac..d0a3f72351d236ba2b76b7e7c9cd47abd6a56ee5 100644
*** a/src/backend/optimizer/util/plancat.c
--- b/src/backend/optimizer/util/plancat.c
*************** get_relation_info(PlannerInfo *root, Oid
*** 166,174 ****
* Ignore invalid indexes, since they can't safely be used for
* queries. Note that this is OK because the data structure we
* are constructing is only used by the planner --- the executor
! * still needs to insert into "invalid" indexes!
*/
! if (!index->indisvalid)
{
index_close(indexRelation, NoLock);
continue;
--- 166,175 ----
* Ignore invalid indexes, since they can't safely be used for
* queries. Note that this is OK because the data structure we
* are constructing is only used by the planner --- the executor
! * still needs to insert into "invalid" indexes, if they're marked
! * IndexIsReady.
*/
! if (!IndexIsValid(index))
{
index_close(indexRelation, NoLock);
continue;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index ae0ef91eb7fdb078f1187886fbb0523ae994e51c..9c39bacc64fb45979b678115942d456b8d2a6878 100644
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
*************** transformIndexConstraint(Constraint *con
*** 1525,1542 ****
index_name, RelationGetRelationName(heap_rel)),
parser_errposition(cxt->pstate, constraint->location)));
! if (!index_form->indisvalid)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("index \"%s\" is not valid", index_name),
parser_errposition(cxt->pstate, constraint->location)));
- if (!index_form->indisready)
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("index \"%s\" is not ready", index_name),
- parser_errposition(cxt->pstate, constraint->location)));
-
if (!index_form->indisunique)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
--- 1525,1536 ----
index_name, RelationGetRelationName(heap_rel)),
parser_errposition(cxt->pstate, constraint->location)));
! if (!IndexIsValid(index_form))
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("index \"%s\" is not valid", index_name),
parser_errposition(cxt->pstate, constraint->location)));
if (!index_form->indisunique)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 6026599a9bc5e089d4c89db3458a8994dc70d949..12a02b8c09b22bb21af897609129fdbb5d6e0433 100644
*** a/src/backend/utils/cache/relcache.c
--- b/src/backend/utils/cache/relcache.c
*************** RelationReloadIndexInfo(Relation relatio
*** 1731,1739 ****
--- 1731,1752 ----
RelationGetRelid(relation));
index = (Form_pg_index) GETSTRUCT(tuple);
+ /*
+ * Basically, let's just copy all the bool fields. There are one or
+ * two of these that can't actually change in the current code, but
+ * it's not worth it to track exactly which ones they are. None of
+ * the array fields are allowed to change, though.
+ */
+ relation->rd_index->indisunique = index->indisunique;
+ relation->rd_index->indisprimary = index->indisprimary;
+ relation->rd_index->indisexclusion = index->indisexclusion;
+ relation->rd_index->indimmediate = index->indimmediate;
+ relation->rd_index->indisclustered = index->indisclustered;
relation->rd_index->indisvalid = index->indisvalid;
relation->rd_index->indcheckxmin = index->indcheckxmin;
relation->rd_index->indisready = index->indisready;
+
+ /* Copy xmin too, as that is needed to make sense of indcheckxmin */
HeapTupleHeaderSetXmin(relation->rd_indextuple->t_data,
HeapTupleHeaderGetXmin(tuple->t_data));
*************** RelationGetIndexList(Relation relation)
*** 3355,3361 ****
result = insert_ordered_oid(result, index->indexrelid);
/* Check to see if it is a unique, non-partial btree index on OID */
! if (index->indnatts == 1 &&
index->indisunique && index->indimmediate &&
index->indkey.values[0] == ObjectIdAttributeNumber &&
index->indclass.values[0] == OID_BTREE_OPS_OID &&
--- 3368,3375 ----
result = insert_ordered_oid(result, index->indexrelid);
/* Check to see if it is a unique, non-partial btree index on OID */
! if (IndexIsValid(index) &&
! index->indnatts == 1 &&
index->indisunique && index->indimmediate &&
index->indkey.values[0] == ObjectIdAttributeNumber &&
index->indclass.values[0] == OID_BTREE_OPS_OID &&
*************** RelationGetIndexAttrBitmap(Relation rela
*** 3662,3667 ****
--- 3676,3686 ----
/*
* For each index, add referenced attributes to indexattrs.
+ *
+ * Note: we consider all indexes returned by RelationGetIndexList, even if
+ * they are not indisready or indisvalid. This is important because an
+ * index for which CREATE INDEX CONCURRENTLY has just started must be
+ * included in HOT-safety decisions (see README.HOT).
*/
indexattrs = NULL;
foreach(l, indexoidlist)
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index 4172f0e4006ddd989d13c993edef3be72b48dbc4..9c9987e8765408d3365642686ed4291ed61913be 100644
*** a/src/include/catalog/index.h
--- b/src/include/catalog/index.h
*************** typedef void (*IndexBuildCallback) (Rela
*** 27,32 ****
--- 27,39 ----
bool tupleIsAlive,
void *state);
+ /* Action code for index_set_state_flags */
+ typedef enum
+ {
+ INDEX_CREATE_SET_READY,
+ INDEX_CREATE_SET_VALID
+ } IndexStateFlagsAction;
+
extern void index_check_primary_key(Relation heapRel,
IndexInfo *indexInfo,
*************** extern double IndexBuildHeapScan(Relatio
*** 88,93 ****
--- 95,102 ----
extern void validate_index(Oid heapId, Oid indexId, Snapshot snapshot);
+ extern void index_set_state_flags(Oid indexId, IndexStateFlagsAction action);
+
extern void reindex_index(Oid indexId, bool skip_constraint_checks);
/* Flag bits for reindex_relation(): */
diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h
index 9f446a5547ec7633fcea73c91c32acfcfe2552f0..dc372b82e26117bedf18dde58bfc95c9e3c9eeb9 100644
*** a/src/include/catalog/pg_index.h
--- b/src/include/catalog/pg_index.h
*************** typedef FormData_pg_index *Form_pg_index
*** 92,95 ****
--- 92,103 ----
#define INDOPTION_DESC 0x0001 /* values are in reverse order */
#define INDOPTION_NULLS_FIRST 0x0002 /* NULLs are first instead of last */
+ /*
+ * Use of these macros is recommended over direct examination of the state
+ * flag columns where possible; this allows source code compatibility with
+ * 9.2 and up.
+ */
+ #define IndexIsValid(indexForm) ((indexForm)->indisvalid)
+ #define IndexIsReady(indexForm) ((indexForm)->indisready)
+
#endif /* PG_INDEX_H */
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers