1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-02 04:21:28 +03:00

Fix assorted bugs in CREATE/DROP INDEX CONCURRENTLY.

Commit 8cb53654db, which introduced DROP
INDEX CONCURRENTLY, managed to break CREATE INDEX CONCURRENTLY via a poor
choice of catalog state representation.  The pg_index state for an index
that's reached the final pre-drop stage was the same as the state for an
index just created by CREATE INDEX CONCURRENTLY.  This meant that the
(necessary) change to make RelationGetIndexList ignore about-to-die indexes
also made it ignore freshly-created indexes; which is catastrophic because
the latter do need to be considered in HOT-safety decisions.  Failure to
do so leads to incorrect index entries and subsequently wrong results from
queries depending on the concurrently-created index.

To fix, add an additional boolean column "indislive" to pg_index, so that
the freshly-created and about-to-die states can be distinguished.  (This
change obviously is only possible in HEAD.  This patch will need to be
back-patched, but in 9.2 we'll use a kluge consisting of overloading the
formerly-impossible state of indisvalid = true and indisready = false.)

In addition, change CREATE/DROP INDEX CONCURRENTLY so that the pg_index
flag changes they make without exclusive lock on the index are made via
heap_inplace_update() rather than a normal transactional update.  The
latter is not very safe because moving the pg_index tuple could result in
concurrent SnapshotNow scans finding it twice or not at all, thus possibly
resulting in index corruption.  This is a pre-existing bug in CREATE INDEX
CONCURRENTLY, which was copied into the DROP code.

In addition, fix various places in the code that ought to check to make
sure that the indexes they are manipulating are valid and/or ready as
appropriate.  These represent bugs that have existed since 8.2, since
a failed CREATE INDEX CONCURRENTLY could leave a corrupt or invalid
index behind, and we ought not try to do anything that might fail with
such an index.

Also fix RelationReloadIndexInfo to ensure it copies all the pg_index
columns that are allowed to change after initial creation.  Previously we
could have been left with stale values of some fields in an index relcache
entry.  It's not clear whether this actually had any user-visible
consequences, but it's at least a bug waiting to happen.

In addition, do some code and docs review for DROP INDEX CONCURRENTLY;
some cosmetic code cleanup but mostly addition and revision of comments.

This will need to be back-patched, but in a noticeably different form,
so I'm committing it to HEAD before working on the back-patch.

Problem reported by Amit Kapila, diagnosis by Pavan Deolassee,
fix by Tom Lane and Andres Freund.
This commit is contained in:
Tom Lane
2012-11-28 21:25:27 -05:00
parent 1577b46b7c
commit 3c84046490
17 changed files with 463 additions and 264 deletions

View File

@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201210071
#define CATALOG_VERSION_NO 201211281
#endif

View File

@@ -27,6 +27,15 @@ typedef void (*IndexBuildCallback) (Relation index,
bool tupleIsAlive,
void *state);
/* Action code for index_set_state_flags */
typedef enum
{
INDEX_CREATE_SET_READY,
INDEX_CREATE_SET_VALID,
INDEX_DROP_CLEAR_VALID,
INDEX_DROP_SET_DEAD
} IndexStateFlagsAction;
extern void index_check_primary_key(Relation heapRel,
IndexInfo *indexInfo,
@@ -90,6 +99,8 @@ extern double IndexBuildHeapScan(Relation heapRelation,
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(): */

View File

@@ -41,6 +41,7 @@ CATALOG(pg_index,2610) BKI_WITHOUT_OIDS BKI_SCHEMA_MACRO
bool indisvalid; /* is this index valid for use by queries? */
bool indcheckxmin; /* must we wait for xmin to be old? */
bool indisready; /* is this index ready for inserts? */
bool indislive; /* is this index alive at all? */
/* variable-length fields start here, but we allow direct access to indkey */
int2vector indkey; /* column numbers of indexed cols, or 0 */
@@ -68,7 +69,7 @@ typedef FormData_pg_index *Form_pg_index;
* compiler constants for pg_index
* ----------------
*/
#define Natts_pg_index 17
#define Natts_pg_index 18
#define Anum_pg_index_indexrelid 1
#define Anum_pg_index_indrelid 2
#define Anum_pg_index_indnatts 3
@@ -80,12 +81,13 @@ typedef FormData_pg_index *Form_pg_index;
#define Anum_pg_index_indisvalid 9
#define Anum_pg_index_indcheckxmin 10
#define Anum_pg_index_indisready 11
#define Anum_pg_index_indkey 12
#define Anum_pg_index_indcollation 13
#define Anum_pg_index_indclass 14
#define Anum_pg_index_indoption 15
#define Anum_pg_index_indexprs 16
#define Anum_pg_index_indpred 17
#define Anum_pg_index_indislive 12
#define Anum_pg_index_indkey 13
#define Anum_pg_index_indcollation 14
#define Anum_pg_index_indclass 15
#define Anum_pg_index_indoption 16
#define Anum_pg_index_indexprs 17
#define Anum_pg_index_indpred 18
/*
* Index AMs that support ordered scans must support these two indoption
@@ -95,4 +97,13 @@ typedef FormData_pg_index *Form_pg_index;
#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
* the hacky representation used in 9.2.
*/
#define IndexIsValid(indexForm) ((indexForm)->indisvalid)
#define IndexIsReady(indexForm) ((indexForm)->indisready)
#define IndexIsLive(indexForm) ((indexForm)->indislive)
#endif /* PG_INDEX_H */