Commit bc1088c2 authored by Tom Lane's avatar Tom Lane

Get rid of bogus use of heap_mark4update in reindex operations (cf.

recent bug report).  Fix processing of nailed-in-cache indexes;
it appears that REINDEX DATABASE has been broken for months :-(.
parent df3e7b3a
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.199 2002/09/22 23:03:58 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.200 2002/09/23 00:42:48 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -1020,96 +1020,36 @@ FormIndexDatum(IndexInfo *indexInfo, ...@@ -1020,96 +1020,36 @@ FormIndexDatum(IndexInfo *indexInfo,
} }
/* --------------------------------------------
* Lock class info for update
* --------------------------------------------
*/
static bool
LockClassinfoForUpdate(Oid relid, HeapTuple rtup,
Buffer *buffer, bool confirmCommitted)
{
HeapTuple classTuple;
bool test;
Relation relationRelation;
/*
* NOTE: get and hold RowExclusiveLock on pg_class, because caller
* will probably modify the rel's pg_class tuple later on.
*/
relationRelation = heap_openr(RelationRelationName, RowExclusiveLock);
classTuple = SearchSysCache(RELOID, PointerGetDatum(relid),
0, 0, 0);
if (!HeapTupleIsValid(classTuple))
{
heap_close(relationRelation, NoLock);
return false;
}
rtup->t_self = classTuple->t_self;
ReleaseSysCache(classTuple);
while (1)
{
ItemPointerData tidsave;
ItemPointerCopy(&(rtup->t_self), &tidsave);
test = heap_mark4update(relationRelation, rtup, buffer,
GetCurrentCommandId());
switch (test)
{
case HeapTupleSelfUpdated:
case HeapTupleMayBeUpdated:
break;
case HeapTupleUpdated:
ReleaseBuffer(*buffer);
if (!ItemPointerEquals(&(rtup->t_self), &tidsave))
continue;
default:
elog(ERROR, "LockClassinfoForUpdate couldn't lock relid %u", relid);
return false;
}
break;
}
CacheInvalidateHeapTuple(relationRelation, rtup);
if (confirmCommitted)
{
HeapTupleHeader th = rtup->t_data;
if (!(th->t_infomask & HEAP_XMIN_COMMITTED))
elog(ERROR, "The tuple isn't committed");
if (th->t_infomask & HEAP_XMAX_COMMITTED)
if (!(th->t_infomask & HEAP_MARKED_FOR_UPDATE))
elog(ERROR, "The tuple is already deleted");
}
heap_close(relationRelation, NoLock);
return true;
}
/* --------------------------------------------- /* ---------------------------------------------
* Indexes of the relation active ? * Indexes of the relation active ?
*
* Caller must hold an adequate lock on the relation to ensure the
* answer won't be changing.
* --------------------------------------------- * ---------------------------------------------
*/ */
bool bool
IndexesAreActive(Oid relid, bool confirmCommitted) IndexesAreActive(Relation heaprel)
{ {
HeapTupleData tuple; bool isactive;
Relation indexRelation; Relation indexRelation;
Buffer buffer;
HeapScanDesc scan; HeapScanDesc scan;
ScanKeyData entry; ScanKeyData entry;
bool isactive;
if (!LockClassinfoForUpdate(relid, &tuple, &buffer, confirmCommitted)) if (heaprel->rd_rel->relkind != RELKIND_RELATION &&
elog(ERROR, "IndexesAreActive couldn't lock %u", relid); heaprel->rd_rel->relkind != RELKIND_TOASTVALUE)
if (((Form_pg_class) GETSTRUCT(&tuple))->relkind != RELKIND_RELATION && elog(ERROR, "relation %s isn't an indexable relation",
((Form_pg_class) GETSTRUCT(&tuple))->relkind != RELKIND_TOASTVALUE) RelationGetRelationName(heaprel));
elog(ERROR, "relation %u isn't an indexable relation", relid);
isactive = ((Form_pg_class) GETSTRUCT(&tuple))->relhasindex; /* If pg_class.relhasindex is set, indexes are active */
ReleaseBuffer(buffer); isactive = heaprel->rd_rel->relhasindex;
if (isactive) if (isactive)
return isactive; return isactive;
/* Otherwise, look to see if there are any indexes */
indexRelation = heap_openr(IndexRelationName, AccessShareLock); indexRelation = heap_openr(IndexRelationName, AccessShareLock);
ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indrelid, ScanKeyEntryInitialize(&entry, 0,
F_OIDEQ, ObjectIdGetDatum(relid)); Anum_pg_index_indrelid, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(heaprel)));
scan = heap_beginscan(indexRelation, SnapshotNow, 1, &entry); scan = heap_beginscan(indexRelation, SnapshotNow, 1, &entry);
if (heap_getnext(scan, ForwardScanDirection) == NULL) if (heap_getnext(scan, ForwardScanDirection) == NULL)
isactive = true; /* no indexes, so report "active" */ isactive = true; /* no indexes, so report "active" */
...@@ -1235,65 +1175,96 @@ setRelhasindex(Oid relid, bool hasindex, bool isprimary, Oid reltoastidxid) ...@@ -1235,65 +1175,96 @@ setRelhasindex(Oid relid, bool hasindex, bool isprimary, Oid reltoastidxid)
heap_close(pg_class, RowExclusiveLock); heap_close(pg_class, RowExclusiveLock);
} }
/*
* setNewRelfilenode - assign a new relfilenode value to the relation
*
* Caller must already hold exclusive lock on the relation.
*/
void void
setNewRelfilenode(Relation relation) setNewRelfilenode(Relation relation)
{ {
Relation pg_class;
Oid newrelfilenode; Oid newrelfilenode;
bool in_place_update = false; Relation pg_class;
HeapTupleData lockTupleData; HeapTuple tuple;
HeapTuple classTuple = NULL; Form_pg_class rd_rel;
Buffer buffer; HeapScanDesc pg_class_scan = NULL;
bool in_place_upd;
RelationData workrel; RelationData workrel;
Assert(!IsSystemRelation(relation) || IsToastRelation(relation) || Assert(!IsSystemRelation(relation) || IsToastRelation(relation) ||
relation->rd_rel->relkind == RELKIND_INDEX); relation->rd_rel->relkind == RELKIND_INDEX);
pg_class = heap_openr(RelationRelationName, RowExclusiveLock);
/* Fetch and lock the classTuple associated with this relation */
if (!LockClassinfoForUpdate(relation->rd_id, &lockTupleData, &buffer, true))
elog(ERROR, "setNewRelfilenode impossible to lock class tuple");
if (IsIgnoringSystemIndexes())
in_place_update = true;
/* Allocate a new relfilenode */ /* Allocate a new relfilenode */
newrelfilenode = newoid(); newrelfilenode = newoid();
/* update pg_class tuple with new relfilenode */
if (!in_place_update) /*
* Find the RELATION relation tuple for the given relation.
*/
pg_class = heap_openr(RelationRelationName, RowExclusiveLock);
in_place_upd = IsIgnoringSystemIndexes();
if (!in_place_upd)
{
tuple = SearchSysCacheCopy(RELOID,
ObjectIdGetDatum(RelationGetRelid(relation)),
0, 0, 0);
}
else
{ {
classTuple = heap_copytuple(&lockTupleData); ScanKeyData key[1];
ReleaseBuffer(buffer);
((Form_pg_class) GETSTRUCT(classTuple))->relfilenode = newrelfilenode; ScanKeyEntryInitialize(&key[0], 0,
simple_heap_update(pg_class, &classTuple->t_self, classTuple); ObjectIdAttributeNumber,
F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key);
tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
} }
if (!HeapTupleIsValid(tuple))
elog(ERROR, "setNewRelfilenode: cannot find relation %u in pg_class",
RelationGetRelid(relation));
rd_rel = (Form_pg_class) GETSTRUCT(tuple);
/* schedule unlinking old relfilenode */ /* schedule unlinking old relfilenode */
smgrunlink(DEFAULT_SMGR, relation); smgrunlink(DEFAULT_SMGR, relation);
/* create another storage file. Is it a little ugly ? */ /* create another storage file. Is it a little ugly ? */
memcpy((char *) &workrel, relation, sizeof(RelationData)); memcpy((char *) &workrel, relation, sizeof(RelationData));
workrel.rd_fd = -1;
workrel.rd_node.relNode = newrelfilenode; workrel.rd_node.relNode = newrelfilenode;
heap_storage_create(&workrel); heap_storage_create(&workrel);
smgrclose(DEFAULT_SMGR, &workrel); smgrclose(DEFAULT_SMGR, &workrel);
/* update pg_class tuple with new relfilenode in place */
if (in_place_update) /* update the pg_class row */
if (in_place_upd)
{ {
classTuple = &lockTupleData; LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE);
rd_rel->relfilenode = newrelfilenode;
LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
WriteNoReleaseBuffer(pg_class_scan->rs_cbuf);
BufferSync();
/* Send out shared cache inval if necessary */ /* Send out shared cache inval if necessary */
if (!IsBootstrapProcessingMode()) if (!IsBootstrapProcessingMode())
CacheInvalidateHeapTuple(pg_class, classTuple); CacheInvalidateHeapTuple(pg_class, tuple);
/* Update the buffer in-place */
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
((Form_pg_class) GETSTRUCT(classTuple))->relfilenode = newrelfilenode;
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
WriteBuffer(buffer);
BufferSync();
} }
/* Keep the catalog indexes up to date */ else
if (!in_place_update) {
CatalogUpdateIndexes(pg_class, classTuple); rd_rel->relfilenode = newrelfilenode;
simple_heap_update(pg_class, &tuple->t_self, tuple);
CatalogUpdateIndexes(pg_class, tuple);
}
if (!pg_class_scan)
heap_freetuple(tuple);
else
heap_endscan(pg_class_scan);
heap_close(pg_class, RowExclusiveLock);
heap_close(pg_class, NoLock); /* Make sure the relfilenode change is visible */
if (!in_place_update)
heap_freetuple(classTuple);
/* Make sure the relfilenode change */
CommandCounterIncrement(); CommandCounterIncrement();
} }
...@@ -1312,7 +1283,6 @@ UpdateStats(Oid relid, double reltuples) ...@@ -1312,7 +1283,6 @@ UpdateStats(Oid relid, double reltuples)
Relation whichRel; Relation whichRel;
Relation pg_class; Relation pg_class;
HeapTuple tuple; HeapTuple tuple;
HeapTuple newtup;
BlockNumber relpages; BlockNumber relpages;
Form_pg_class rd_rel; Form_pg_class rd_rel;
HeapScanDesc pg_class_scan = NULL; HeapScanDesc pg_class_scan = NULL;
...@@ -1430,13 +1400,10 @@ UpdateStats(Oid relid, double reltuples) ...@@ -1430,13 +1400,10 @@ UpdateStats(Oid relid, double reltuples)
else else
{ {
/* During normal processing, must work harder. */ /* During normal processing, must work harder. */
newtup = heap_copytuple(tuple);
rd_rel = (Form_pg_class) GETSTRUCT(newtup);
rd_rel->relpages = (int32) relpages; rd_rel->relpages = (int32) relpages;
rd_rel->reltuples = (float4) reltuples; rd_rel->reltuples = (float4) reltuples;
simple_heap_update(pg_class, &tuple->t_self, newtup); simple_heap_update(pg_class, &tuple->t_self, tuple);
CatalogUpdateIndexes(pg_class, newtup); CatalogUpdateIndexes(pg_class, tuple);
heap_freetuple(newtup);
} }
} }
...@@ -1806,8 +1773,6 @@ reindex_index(Oid indexId, bool force, bool inplace) ...@@ -1806,8 +1773,6 @@ reindex_index(Oid indexId, bool force, bool inplace)
/* Get OID of index's parent table */ /* Get OID of index's parent table */
heapId = iRel->rd_index->indrelid; heapId = iRel->rd_index->indrelid;
/* Fetch info needed for index_build */
indexInfo = BuildIndexInfo(iRel->rd_index);
/* Open the parent heap relation */ /* Open the parent heap relation */
heapRelation = heap_open(heapId, AccessExclusiveLock); heapRelation = heap_open(heapId, AccessExclusiveLock);
...@@ -1815,11 +1780,29 @@ reindex_index(Oid indexId, bool force, bool inplace) ...@@ -1815,11 +1780,29 @@ reindex_index(Oid indexId, bool force, bool inplace)
elog(ERROR, "reindex_index: can't open heap relation"); elog(ERROR, "reindex_index: can't open heap relation");
/* /*
* Force inplace processing if it's a shared index. Necessary because * If it's a shared index, we must do inplace processing (because we
* we have no way to update relfilenode in other databases. * have no way to update relfilenode in other databases). Also, if
* it's a nailed-in-cache index, we must do inplace processing because
* the relcache can't cope with changing its relfilenode.
*
* In either of these cases, we are definitely processing a system
* index, so we'd better be ignoring system indexes.
*/ */
if (iRel->rd_rel->relisshared) if (iRel->rd_rel->relisshared)
{
if (!IsIgnoringSystemIndexes())
elog(ERROR, "the target relation %u is shared", indexId);
inplace = true;
}
if (iRel->rd_isnailed)
{
if (!IsIgnoringSystemIndexes())
elog(ERROR, "the target relation %u is nailed", indexId);
inplace = true; inplace = true;
}
/* Fetch info needed for index_build */
indexInfo = BuildIndexInfo(iRel->rd_index);
if (inplace) if (inplace)
{ {
...@@ -1859,22 +1842,25 @@ reindex_index(Oid indexId, bool force, bool inplace) ...@@ -1859,22 +1842,25 @@ reindex_index(Oid indexId, bool force, bool inplace)
* ---------------------------- * ----------------------------
* activate_indexes_of_a_table * activate_indexes_of_a_table
* activate/deactivate indexes of the specified table. * activate/deactivate indexes of the specified table.
*
* Caller must already hold exclusive lock on the table.
* ---------------------------- * ----------------------------
*/ */
bool bool
activate_indexes_of_a_table(Oid relid, bool activate) activate_indexes_of_a_table(Relation heaprel, bool activate)
{ {
if (IndexesAreActive(relid, true)) if (IndexesAreActive(heaprel))
{ {
if (!activate) if (!activate)
setRelhasindex(relid, false, false, InvalidOid); setRelhasindex(RelationGetRelid(heaprel), false, false,
InvalidOid);
else else
return false; return false;
} }
else else
{ {
if (activate) if (activate)
reindex_relation(relid, false); reindex_relation(RelationGetRelid(heaprel), false);
else else
return false; return false;
} }
...@@ -1896,18 +1882,10 @@ reindex_relation(Oid relid, bool force) ...@@ -1896,18 +1882,10 @@ reindex_relation(Oid relid, bool force)
bool old, bool old,
reindexed; reindexed;
bool deactivate_needed, bool deactivate_needed,
overwrite, overwrite;
upd_pg_class_inplace;
Relation rel; Relation rel;
overwrite = upd_pg_class_inplace = deactivate_needed = false; overwrite = deactivate_needed = false;
/*
* avoid heap_update() pg_class tuples while processing reindex for
* pg_class.
*/
if (IsIgnoringSystemIndexes())
upd_pg_class_inplace = true;
/* /*
* Ensure to hold an exclusive lock throughout the transaction. The * Ensure to hold an exclusive lock throughout the transaction. The
...@@ -1923,23 +1901,6 @@ reindex_relation(Oid relid, bool force) ...@@ -1923,23 +1901,6 @@ reindex_relation(Oid relid, bool force)
if (!IsIgnoringSystemIndexes() && if (!IsIgnoringSystemIndexes() &&
IsSystemRelation(rel) && !IsToastRelation(rel)) IsSystemRelation(rel) && !IsToastRelation(rel))
deactivate_needed = true; deactivate_needed = true;
#ifndef ENABLE_REINDEX_NAILED_RELATIONS
/*
* nailed relations are never updated. We couldn't keep the
* consistency between the relation descriptors and pg_class tuples.
*/
if (rel->rd_isnailed)
{
if (IsIgnoringSystemIndexes())
{
overwrite = true;
deactivate_needed = true;
}
else
elog(ERROR, "the target relation %u is nailed", relid);
}
#endif /* ENABLE_REINDEX_NAILED_RELATIONS */
/* /*
* Shared system indexes must be overwritten because it's impossible * Shared system indexes must be overwritten because it's impossible
...@@ -1956,26 +1917,28 @@ reindex_relation(Oid relid, bool force) ...@@ -1956,26 +1917,28 @@ reindex_relation(Oid relid, bool force)
elog(ERROR, "the target relation %u is shared", relid); elog(ERROR, "the target relation %u is shared", relid);
} }
/*
* Continue to hold the lock.
*/
heap_close(rel, NoLock);
old = SetReindexProcessing(true); old = SetReindexProcessing(true);
if (deactivate_needed) if (deactivate_needed)
{ {
if (IndexesAreActive(relid, upd_pg_class_inplace)) if (IndexesAreActive(rel))
{ {
if (!force) if (!force)
{ {
SetReindexProcessing(old); SetReindexProcessing(old);
heap_close(rel, NoLock);
return false; return false;
} }
activate_indexes_of_a_table(relid, false); activate_indexes_of_a_table(rel, false);
CommandCounterIncrement(); CommandCounterIncrement();
} }
} }
/*
* Continue to hold the lock.
*/
heap_close(rel, NoLock);
indexRelation = heap_openr(IndexRelationName, AccessShareLock); indexRelation = heap_openr(IndexRelationName, AccessShareLock);
ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indrelid, ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indrelid,
F_OIDEQ, ObjectIdGetDatum(relid)); F_OIDEQ, ObjectIdGetDatum(relid));
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.89 2002/09/19 23:40:56 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.90 2002/09/23 00:42:48 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -110,7 +110,7 @@ DefineIndex(RangeVar *heapRelation, ...@@ -110,7 +110,7 @@ DefineIndex(RangeVar *heapRelation,
if (!IsBootstrapProcessingMode() && if (!IsBootstrapProcessingMode() &&
IsSystemRelation(rel) && IsSystemRelation(rel) &&
!IndexesAreActive(relationId, false)) !IndexesAreActive(rel))
elog(ERROR, "Existing indexes are inactive. REINDEX first"); elog(ERROR, "Existing indexes are inactive. REINDEX first");
heap_close(rel, NoLock); heap_close(rel, NoLock);
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.238 2002/09/20 19:56:01 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.239 2002/09/23 00:42:48 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -894,7 +894,7 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt) ...@@ -894,7 +894,7 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
{ {
vac_close_indexes(nindexes, Irel); vac_close_indexes(nindexes, Irel);
Irel = (Relation *) NULL; Irel = (Relation *) NULL;
activate_indexes_of_a_table(RelationGetRelid(onerel), false); activate_indexes_of_a_table(onerel, false);
} }
#endif /* NOT_USED */ #endif /* NOT_USED */
...@@ -947,7 +947,7 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt) ...@@ -947,7 +947,7 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
#ifdef NOT_USED #ifdef NOT_USED
if (reindex) if (reindex)
activate_indexes_of_a_table(RelationGetRelid(onerel), true); activate_indexes_of_a_table(onerel, true);
#endif /* NOT_USED */ #endif /* NOT_USED */
/* update shared free space map with final free space info */ /* update shared free space map with final free space info */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: index.h,v 1.49 2002/07/12 18:43:19 tgl Exp $ * $Id: index.h,v 1.50 2002/09/23 00:42:48 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -50,7 +50,7 @@ extern void FormIndexDatum(IndexInfo *indexInfo, ...@@ -50,7 +50,7 @@ extern void FormIndexDatum(IndexInfo *indexInfo,
char *nullv); char *nullv);
extern void UpdateStats(Oid relid, double reltuples); extern void UpdateStats(Oid relid, double reltuples);
extern bool IndexesAreActive(Oid relid, bool comfirmCommitted); extern bool IndexesAreActive(Relation heaprel);
extern void setRelhasindex(Oid relid, bool hasindex, extern void setRelhasindex(Oid relid, bool hasindex,
bool isprimary, Oid reltoastidxid); bool isprimary, Oid reltoastidxid);
...@@ -68,8 +68,9 @@ extern double IndexBuildHeapScan(Relation heapRelation, ...@@ -68,8 +68,9 @@ extern double IndexBuildHeapScan(Relation heapRelation,
IndexBuildCallback callback, IndexBuildCallback callback,
void *callback_state); void *callback_state);
extern bool activate_indexes_of_a_table(Relation heaprel, bool activate);
extern bool reindex_index(Oid indexId, bool force, bool inplace); extern bool reindex_index(Oid indexId, bool force, bool inplace);
extern bool activate_indexes_of_a_table(Oid relid, bool activate);
extern bool reindex_relation(Oid relid, bool force); extern bool reindex_relation(Oid relid, bool force);
#endif /* INDEX_H */ #endif /* INDEX_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment