diff options
Diffstat (limited to 'src/backend/catalog/index.c')
-rw-r--r-- | src/backend/catalog/index.c | 223 |
1 files changed, 93 insertions, 130 deletions
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 33aa67fe451..3833c961f40 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.128 2000/10/11 21:28:18 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.129 2000/11/08 22:09:56 tgl Exp $ * * * INTERFACE ROUTINES @@ -26,6 +26,7 @@ #include "access/heapam.h" #include "access/istrat.h" #include "bootstrap/bootstrap.h" +#include "catalog/catalog.h" #include "catalog/catname.h" #include "catalog/heap.h" #include "catalog/index.h" @@ -43,10 +44,10 @@ #include "utils/builtins.h" #include "utils/catcache.h" #include "utils/fmgroids.h" +#include "utils/inval.h" #include "utils/relcache.h" #include "utils/syscache.h" #include "utils/temprel.h" -#include "utils/inval.h" /* * macros used in guessing how many tuples are on a page. @@ -927,6 +928,13 @@ index_create(char *heapRelationName, indexRelation = heap_create(indexRelationName, indexTupDesc, istemp, false, allow_system_table_mods); + /* + * Obtain exclusive lock on it. Although no other backends can see it + * until we commit, this prevents deadlock-risk complaints from lock + * manager in cases such as CLUSTER. + */ + LockRelation(indexRelation, AccessExclusiveLock); + /* ---------------- * construct the index relation descriptor * @@ -990,7 +998,8 @@ index_create(char *heapRelationName, * * In normal processing mode, the heap and index relations are closed by * index_build() --- but we continue to hold the ShareLock on the heap - * that we acquired above, until end of transaction. + * and the exclusive lock on the index that we acquired above, until + * end of transaction. */ if (IsBootstrapProcessingMode()) { @@ -1020,6 +1029,7 @@ index_drop(Oid indexId) Relation attributeRelation; HeapTuple tuple; int16 attnum; + int i; Assert(OidIsValid(indexId)); @@ -1040,19 +1050,11 @@ index_drop(Oid indexId) LockRelation(userIndexRelation, AccessExclusiveLock); /* ---------------- - * DROP INDEX within a transaction block is dangerous, because - * if the transaction is later rolled back there will be no way to - * undo the unlink of the relation's physical file. For now, allow it - * but emit a warning message. - * Someday we might want to consider postponing the physical unlink - * until transaction commit, but that's a lot of work... - * The only case that actually works right is for relations created - * in the current transaction, since the post-abort state would be that - * they don't exist anyway. So, no warning in that case. + * Note: unlike heap_drop_with_catalog, we do not need to prevent + * deletion of system indexes here; that's checked for upstream. + * If we did check it here, deletion of TOAST tables would fail... * ---------------- */ - if (IsTransactionBlock() && !userIndexRelation->rd_myxactonly) - elog(NOTICE, "Caution: DROP INDEX cannot be rolled back, so don't abort now"); /* ---------------- * fix DESCRIPTION relation @@ -1077,20 +1079,14 @@ index_drop(Oid indexId) heap_freetuple(tuple); /* - * Find the pg_class tuple for the owning relation. We do not attempt - * to clear relhasindex, since we are too lazy to test whether any other - * indexes remain (the next VACUUM will fix it if necessary). But we - * must send out a shared-cache-inval notice on the owning relation - * to ensure other backends update their relcache lists of indexes. + * Update the pg_class tuple for the owning relation. We are presently + * too lazy to attempt to compute the new correct value of relhasindex + * (the next VACUUM will fix it if necessary). But we must send out a + * shared-cache-inval notice on the owning relation to ensure other + * backends update their relcache lists of indexes. So, unconditionally + * do setRelhasindex(true). */ - tuple = SearchSysCacheTupleCopy(RELOID, - ObjectIdGetDatum(heapId), - 0, 0, 0); - - Assert(HeapTupleIsValid(tuple)); - - ImmediateInvalidateSharedHeapTuple(relationRelation, tuple); - heap_freetuple(tuple); + setRelhasindex(heapId, true); heap_close(relationRelation, RowExclusiveLock); @@ -1131,10 +1127,11 @@ index_drop(Oid indexId) /* * flush buffer cache and physically remove the file */ - ReleaseRelationBuffers(userIndexRelation); + i = FlushRelationBuffers(userIndexRelation, (BlockNumber) 0); + if (i < 0) + elog(ERROR, "index_drop: FlushRelationBuffers returned %d", i); - if (smgrunlink(DEFAULT_SMGR, userIndexRelation) != SM_SUCCESS) - elog(ERROR, "index_drop: unlink: %m"); + smgrunlink(DEFAULT_SMGR, userIndexRelation); /* * Close rels, but keep locks @@ -1144,7 +1141,7 @@ index_drop(Oid indexId) RelationForgetRelation(indexId); - /* does something only if it is a temp index */ + /* if it's a temp index, clear the temp mapping table entry */ remove_temp_rel_by_relid(indexId); } @@ -1331,7 +1328,11 @@ LockClassinfoForUpdate(Oid relid, HeapTuple rtup, return false; rtup->t_self = classTuple->t_self; pgcform = (Form_pg_class) GETSTRUCT(classTuple); - relationRelation = heap_openr(RelationRelationName, RowShareLock); + /* + * 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); test = heap_mark4update(relationRelation, rtup, buffer); switch (test) { @@ -1388,57 +1389,38 @@ IndexesAreActive(Oid relid, bool confirmCommitted) if (!heap_getnext(scan, 0)) isactive = true; heap_endscan(scan); - heap_close(indexRelation, NoLock); + heap_close(indexRelation, AccessShareLock); return isactive; } /* ---------------- - * set relhasindex of pg_class in place + * set relhasindex of relation's pg_class entry + * + * NOTE: an important side-effect of this operation is that an SI invalidation + * message is sent out to all backends --- including me --- causing relcache + * entries to be flushed or updated with the new hasindex data. + * Therefore, we execute the update even if relhasindex has the right value + * already. Possible future improvement: skip the disk update and just send + * an SI message in that case. * ---------------- */ void -setRelhasindexInplace(Oid relid, bool hasindex, bool immediate) +setRelhasindex(Oid relid, bool hasindex) { - Relation whichRel; Relation pg_class; HeapTuple tuple; - Form_pg_class rd_rel; HeapScanDesc pg_class_scan = NULL; - /* ---------------- - * This routine handles updates for only the heap relation - * hasindex. In order to guarantee that we're able to *see* the index - * relation tuple, we bump the command counter id here. - * ---------------- - */ - CommandCounterIncrement(); - - /* ---------------- - * CommandCounterIncrement() flushes invalid cache entries, including - * those for the heap and index relations for which we're updating - * statistics. Now that the cache is flushed, it's safe to open the - * relation again. We need the relation open in order to figure out - * how many blocks it contains. - * ---------------- - */ - - whichRel = heap_open(relid, ShareLock); - - if (!RelationIsValid(whichRel)) - elog(ERROR, "setRelhasindexInplace: cannot open relation id %u", relid); - - /* ---------------- - * Find the RELATION relation tuple for the given relation. - * ---------------- + /* + * Find the tuple to update in pg_class. */ pg_class = heap_openr(RelationRelationName, RowExclusiveLock); - if (!RelationIsValid(pg_class)) - elog(ERROR, "setRelhasindexInplace: could not open RELATION relation"); if (!IsIgnoringSystemIndexes()) { tuple = SearchSysCacheTupleCopy(RELOID, - ObjectIdGetDatum(relid), 0, 0, 0); + ObjectIdGetDatum(relid), + 0, 0, 0); } else { @@ -1458,72 +1440,46 @@ setRelhasindexInplace(Oid relid, bool hasindex, bool immediate) if (pg_class_scan) heap_endscan(pg_class_scan); heap_close(pg_class, RowExclusiveLock); - elog(ERROR, "setRelhasindexInplace: cannot scan RELATION relation"); - } - - /* - * Confirm that target tuple is locked by this transaction in case of - * immediate updation. - */ - if (immediate) - { - HeapTupleHeader th = tuple->t_data; - - if (!(th->t_infomask & HEAP_XMIN_COMMITTED)) - elog(ERROR, "Immediate hasindex updation can be done only for committed tuples %x", th->t_infomask); - if (th->t_infomask & HEAP_XMAX_INVALID) - elog(ERROR, "Immediate hasindex updation can be done only for locked tuples %x", th->t_infomask); - if (th->t_infomask & HEAP_XMAX_COMMITTED) - elog(ERROR, "Immediate hasindex updation can be done only for locked tuples %x", th->t_infomask); - if (!(th->t_infomask & HEAP_MARKED_FOR_UPDATE)) - elog(ERROR, "Immediate hasindex updation can be done only for locked tuples %x", th->t_infomask); - if (!(TransactionIdIsCurrentTransactionId(th->t_xmax))) - elog(ERROR, "The updating tuple is already locked by another backend"); + elog(ERROR, "setRelhasindex: cannot find relation %u in pg_class", + relid); } - /* - * We shouldn't have to do this, but we do... Modify the reldesc in - * place with the new values so that the cache contains the latest - * copy. - */ - whichRel->rd_rel->relhasindex = hasindex; - /* ---------------- * Update hasindex in pg_class. * ---------------- */ + ((Form_pg_class) GETSTRUCT(tuple))->relhasindex = hasindex; + if (pg_class_scan) { - rd_rel = (Form_pg_class) GETSTRUCT(tuple); - rd_rel->relhasindex = hasindex; + /* Write the modified tuple in-place */ WriteNoReleaseBuffer(pg_class_scan->rs_cbuf); + /* Send out shared cache inval if necessary */ + if (!IsBootstrapProcessingMode()) + RelationInvalidateHeapTuple(pg_class, tuple); } else { - HeapTupleData htup; - Buffer buffer; - - htup.t_self = tuple->t_self; - heap_fetch(pg_class, SnapshotNow, &htup, &buffer); - rd_rel = (Form_pg_class) GETSTRUCT(&htup); - rd_rel->relhasindex = hasindex; - WriteBuffer(buffer); - } + heap_update(pg_class, &tuple->t_self, tuple, NULL); - /* - * Send out a shared-cache-inval message so other backends notice the - * update and fix their syscaches/relcaches. - */ - if (!IsBootstrapProcessingMode()) - ImmediateInvalidateSharedHeapTuple(pg_class, tuple); + /* Keep the catalog indices up to date */ + if (!IsIgnoringSystemIndexes()) + { + Relation idescs[Num_pg_class_indices]; + + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, + idescs); + CatalogIndexInsert(idescs, Num_pg_class_indices, pg_class, tuple); + CatalogCloseIndices(Num_pg_class_indices, idescs); + } + } if (!pg_class_scan) heap_freetuple(tuple); else heap_endscan(pg_class_scan); - heap_close(pg_class, NoLock); - heap_close(whichRel, NoLock); + heap_close(pg_class, RowExclusiveLock); } /* ---------------- @@ -1531,7 +1487,7 @@ setRelhasindexInplace(Oid relid, bool hasindex, bool immediate) * ---------------- */ void -UpdateStats(Oid relid, long reltuples, bool inplace) +UpdateStats(Oid relid, long reltuples) { Relation whichRel; Relation pg_class; @@ -1573,6 +1529,7 @@ UpdateStats(Oid relid, long reltuples, bool inplace) if (!RelationIsValid(whichRel)) elog(ERROR, "UpdateStats: cannot open relation id %u", relid); + /* Grab lock to be held till end of xact (probably redundant...) */ LockRelation(whichRel, ShareLock); /* ---------------- @@ -1580,10 +1537,9 @@ UpdateStats(Oid relid, long reltuples, bool inplace) * ---------------- */ pg_class = heap_openr(RelationRelationName, RowExclusiveLock); - if (!RelationIsValid(pg_class)) - elog(ERROR, "UpdateStats: could not open RELATION relation"); - in_place_upd = (inplace || IsBootstrapProcessingMode()); + in_place_upd = (IsReindexProcessing() || IsBootstrapProcessingMode()); + if (!in_place_upd) { tuple = SearchSysCacheTupleCopy(RELOID, @@ -1608,7 +1564,8 @@ UpdateStats(Oid relid, long reltuples, bool inplace) if (pg_class_scan) heap_endscan(pg_class_scan); heap_close(pg_class, RowExclusiveLock); - elog(ERROR, "UpdateStats: cannot scan RELATION relation"); + elog(ERROR, "UpdateStats: cannot find relation %u in pg_class", + relid); } /* ---------------- @@ -1655,17 +1612,16 @@ UpdateStats(Oid relid, long reltuples, bool inplace) */ if (in_place_upd) { - /* * At bootstrap time, we don't need to worry about concurrency or - * visibility of changes, so we cheat. + * visibility of changes, so we cheat. Also cheat if REINDEX. */ - if (!IsBootstrapProcessingMode()) - ImmediateInvalidateSharedHeapTuple(pg_class, tuple); rd_rel = (Form_pg_class) GETSTRUCT(tuple); rd_rel->relpages = relpages; rd_rel->reltuples = reltuples; WriteNoReleaseBuffer(pg_class_scan->rs_cbuf); + if (!IsBootstrapProcessingMode()) + RelationInvalidateHeapTuple(pg_class, tuple); } else { @@ -1700,7 +1656,7 @@ UpdateStats(Oid relid, long reltuples, bool inplace) heap_close(pg_class, RowExclusiveLock); /* Cheating a little bit since we didn't open it with heap_open... */ - heap_close(whichRel, ShareLock); + heap_close(whichRel, NoLock); } @@ -1868,18 +1824,16 @@ DefaultBuild(Relation heapRelation, { Oid hrelid = RelationGetRelid(heapRelation); Oid irelid = RelationGetRelid(indexRelation); - bool inplace = IsReindexProcessing(); heap_close(heapRelation, NoLock); index_close(indexRelation); - UpdateStats(hrelid, reltuples, inplace); - UpdateStats(irelid, indtuples, inplace); + UpdateStats(hrelid, reltuples); + UpdateStats(irelid, indtuples); if (oldPred != NULL) { if (indtuples == reltuples) predicate = NULL; - if (!inplace) - UpdateIndexPredicate(irelid, oldPred, predicate); + UpdateIndexPredicate(irelid, oldPred, predicate); } } } @@ -1981,6 +1935,15 @@ reindex_index(Oid indexId, bool force) accessMethodId; bool old; + /* ---------------- + * REINDEX within a transaction block is dangerous, because + * if the transaction is later rolled back we have no way to + * undo truncation of the index's physical file. Disallow it. + * ---------------- + */ + if (IsTransactionBlock()) + elog(ERROR, "REINDEX cannot run inside a BEGIN/END block"); + old = SetReindexProcessing(true); /* Scan pg_index to find the index's pg_index entry */ @@ -2024,7 +1987,7 @@ reindex_index(Oid indexId, bool force) * Release any buffers associated with this index. If they're dirty, * they're just dropped without bothering to flush to disk. */ - ReleaseRelationBuffers(iRel); + DropRelationBuffers(iRel); /* Now truncate the actual data and set blocks to zero */ smgrtruncate(DEFAULT_SMGR, iRel, 0); @@ -2056,7 +2019,7 @@ activate_indexes_of_a_table(Oid relid, bool activate) if (IndexesAreActive(relid, true)) { if (!activate) - setRelhasindexInplace(relid, false, true); + setRelhasindex(relid, false); else return false; } @@ -2117,7 +2080,7 @@ reindex_relation(Oid relid, bool force) heap_endscan(scan); heap_close(indexRelation, AccessShareLock); if (reindexed) - setRelhasindexInplace(relid, true, false); + setRelhasindex(relid, true); SetReindexProcessing(old); return reindexed; } |