*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.67 2000/10/05 19:48:20 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.68 2000/11/08 22:09:53 tgl Exp $
*
* NOTES
* some of the executor utility code such as "ExecTypeFromTL" should be
bool
equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
{
- int i;
+ int i,
+ j,
+ n;
if (tupdesc1->natts != tupdesc2->natts)
return false;
/*
* We do not need to check every single field here, and in fact
* some fields such as attdispersion probably shouldn't be
- * compared.
+ * compared. We can also disregard attnum (it was used to
+ * place the row in the attrs array) and everything derived
+ * from the column datatype.
*/
if (strcmp(NameStr(attr1->attname), NameStr(attr2->attname)) != 0)
return false;
if (constr2 == NULL)
return false;
- if (constr1->num_defval != constr2->num_defval)
+ if (constr1->has_not_null != constr2->has_not_null)
+ return false;
+ n = constr1->num_defval;
+ if (n != (int) constr2->num_defval)
return false;
- for (i = 0; i < (int) constr1->num_defval; i++)
+ for (i = 0; i < n; i++)
{
AttrDefault *defval1 = constr1->defval + i;
- AttrDefault *defval2 = constr2->defval + i;
+ AttrDefault *defval2 = constr2->defval;
- if (defval1->adnum != defval2->adnum)
+ /*
+ * We can't assume that the items are always read from the
+ * system catalogs in the same order; so use the adnum field to
+ * identify the matching item to compare.
+ */
+ for (j = 0; j < n; defval2++, j++)
+ {
+ if (defval1->adnum == defval2->adnum)
+ break;
+ }
+ if (j >= n)
return false;
if (strcmp(defval1->adbin, defval2->adbin) != 0)
return false;
}
- if (constr1->num_check != constr2->num_check)
+ n = constr1->num_check;
+ if (n != (int) constr2->num_check)
return false;
- for (i = 0; i < (int) constr1->num_check; i++)
+ for (i = 0; i < n; i++)
{
ConstrCheck *check1 = constr1->check + i;
- ConstrCheck *check2 = constr2->check + i;
+ ConstrCheck *check2 = constr2->check;
- if (strcmp(check1->ccname, check2->ccname) != 0)
- return false;
- if (strcmp(check1->ccbin, check2->ccbin) != 0)
+ /*
+ * Similarly, don't assume that the checks are always read
+ * in the same order; match them up by name and contents.
+ * (The name *should* be unique, but...)
+ */
+ for (j = 0; j < n; check2++, j++)
+ {
+ if (strcmp(check1->ccname, check2->ccname) == 0 &&
+ strcmp(check1->ccbin, check2->ccbin) == 0)
+ break;
+ }
+ if (j >= n)
return false;
}
- if (constr1->has_not_null != constr2->has_not_null)
- return false;
}
else if (tupdesc2->constr != NULL)
return false;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.63 2000/10/21 15:43:09 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.64 2000/11/08 22:09:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
Oid hrelid = RelationGetRelid(heap);
Oid irelid = RelationGetRelid(index);
- bool inplace = IsReindexProcessing();
heap_close(heap, NoLock);
index_close(index);
- UpdateStats(hrelid, nhtups, inplace);
- UpdateStats(irelid, nitups, inplace);
- if (oldPred != NULL && !inplace)
+ UpdateStats(hrelid, nhtups);
+ UpdateStats(irelid, nitups);
+ if (oldPred != NULL)
{
if (nitups == nhtups)
pred = NULL;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.43 2000/10/21 15:43:11 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.44 2000/11/08 22:09:54 tgl Exp $
*
* NOTES
* This file contains only the public interface routines.
{
Oid hrelid = RelationGetRelid(heap);
Oid irelid = RelationGetRelid(index);
- bool inplace = IsReindexProcessing();
heap_close(heap, NoLock);
index_close(index);
- UpdateStats(hrelid, nhtups, inplace);
- UpdateStats(irelid, nitups, inplace);
- if (oldPred != NULL && !inplace)
+ UpdateStats(hrelid, nhtups);
+ UpdateStats(irelid, nitups);
+ if (oldPred != NULL)
{
if (nitups == nhtups)
pred = NULL;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.92 2000/10/29 18:33:39 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.93 2000/11/08 22:09:54 tgl Exp $
*
*
* INTERFACE ROUTINES
/* ----------------
* increment relation ref count while scanning relation
- * ----------------
- */
- RelationIncrementReferenceCount(relation);
-
- /* ----------------
- * Acquire AccessShareLock for the duration of the scan
*
- * Note: we could get an SI inval message here and consequently have
- * to rebuild the relcache entry. The refcount increment above
- * ensures that we will rebuild it and not just flush it...
+ * This is just to make really sure the relcache entry won't go away
+ * while the scan has a pointer to it. Caller should be holding the
+ * rel open anyway, so this is redundant in all normal scenarios...
* ----------------
*/
- LockRelation(relation, AccessShareLock);
+ RelationIncrementReferenceCount(relation);
/* XXX someday assert SelfTimeQual if relkind == RELKIND_UNCATALOGED */
if (relation->rd_rel->relkind == RELKIND_UNCATALOGED)
scan->rs_snapshot = snapshot;
scan->rs_nkeys = (short) nkeys;
+ /*
+ * we do this here instead of in initscan() because heap_rescan
+ * also calls initscan() and we don't want to allocate memory again
+ */
if (nkeys)
-
- /*
- * we do this here instead of in initscan() because heap_rescan
- * also calls initscan() and we don't want to allocate memory
- * again
- */
scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
else
scan->rs_key = NULL;
IncrHeapAccessStat(local_rescan);
IncrHeapAccessStat(global_rescan);
- /* Note: set relation level read lock is still set */
-
/* ----------------
* unpin scan buffers
* ----------------
* reinitialize scan descriptor
* ----------------
*/
- scan->rs_atend = (bool) scanFromEnd;
+ scan->rs_atend = scanFromEnd;
initscan(scan, scan->rs_rd, scanFromEnd, scan->rs_nkeys, key);
}
*/
unpinscan(scan);
- /* ----------------
- * Release AccessShareLock acquired by heap_beginscan()
- * ----------------
- */
- UnlockRelation(scan->rs_rd, AccessShareLock);
-
/* ----------------
* decrement relation reference count and free scan descriptor storage
* ----------------
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.69 2000/11/01 20:39:58 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.70 2000/11/08 22:09:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
Oid hrelid = RelationGetRelid(heap);
Oid irelid = RelationGetRelid(index);
- bool inplace = IsReindexProcessing();
heap_close(heap, NoLock);
index_close(index);
-
- UpdateStats(hrelid, nhtups, inplace);
- UpdateStats(irelid, nitups, inplace);
+ UpdateStats(hrelid, nhtups);
+ UpdateStats(irelid, nitups);
if (oldPred != NULL)
{
if (nitups == nhtups)
pred = NULL;
- if (!inplace)
- UpdateIndexPredicate(irelid, oldPred, pred);
+ UpdateIndexPredicate(irelid, oldPred, pred);
}
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtree.c,v 1.54 2000/10/21 15:43:20 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtree.c,v 1.55 2000/11/08 22:09:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
Oid hrelid = RelationGetRelid(heap);
Oid irelid = RelationGetRelid(index);
- bool inplace = IsReindexProcessing();
heap_close(heap, NoLock);
index_close(index);
- UpdateStats(hrelid, nhtups, inplace);
- UpdateStats(irelid, nitups, inplace);
- if (oldPred != NULL && !inplace)
+ UpdateStats(hrelid, nhtups);
+ UpdateStats(irelid, nitups);
+ if (oldPred != NULL)
{
if (nitups == nhtups)
pred = NULL;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/transam/varsup.c,v 1.31 2000/11/03 11:39:35 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/transam/varsup.c,v 1.32 2000/11/08 22:09:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
TransactionIdStore(xid, &(var->nextXidData));
- FlushBuffer(buf, TRUE);
+ FlushBuffer(buf, true, true);
}
/* --------------------------------
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.80 2000/11/05 22:50:19 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.81 2000/11/08 22:09:55 tgl Exp $
*
* NOTES
* Transaction aborts can now occur two ways:
#include "miscadmin.h"
#include "storage/proc.h"
#include "storage/sinval.h"
+#include "storage/smgr.h"
#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/portal.h"
}
RelationPurgeLocalRelation(true);
+ AtEOXact_temp_relations(true);
+ smgrDoPendingDeletes(true);
+
AtEOXact_SPI();
AtEOXact_nbtree();
AtCommit_Cache();
CloseSequences();
AtEOXact_portals();
RecordTransactionAbort();
+
RelationPurgeLocalRelation(false);
- remove_temp_rel_in_myxid();
+ AtEOXact_temp_relations(false);
+ smgrDoPendingDeletes(false);
+
AtEOXact_SPI();
AtEOXact_nbtree();
AtAbort_Cache();
sprintf(RelationGetPhysicalRelationName(logRelation), "pg_log");
logRelation->rd_node.tblNode = InvalidOid;
logRelation->rd_node.relNode = RelOid_pg_log;
- logRelation->rd_unlinked = false; /* must exists */
logRelation->rd_fd = -1;
- logRelation->rd_fd = smgropen(DEFAULT_SMGR, logRelation);
+ logRelation->rd_fd = smgropen(DEFAULT_SMGR, logRelation, false);
if (logRelation->rd_fd < 0)
elog(STOP, "XLogOpenLogRelation: failed to open pg_log");
LogRelation = logRelation;
hentry->rdesc = res;
- res->reldata.rd_unlinked = true; /* look smgropen */
res->reldata.rd_fd = -1;
- res->reldata.rd_fd = smgropen(DEFAULT_SMGR, &(res->reldata));
+ res->reldata.rd_fd = smgropen(DEFAULT_SMGR, &(res->reldata),
+ true /* allow failure */);
}
res->moreRecently = &(_xlrelarr[0]);
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.96 2000/11/04 12:43:23 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.97 2000/11/08 22:09:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* -mer
*/
if (!BootstrapAlreadySeen(RelationGetRelid(heap)))
- UpdateStats(RelationGetRelid(heap), 0, true);
+ UpdateStats(RelationGetRelid(heap), 0);
/* XXX Probably we ought to close the heap and index here? */
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.150 2000/10/22 23:32:38 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.151 2000/11/08 22:09:56 tgl Exp $
*
*
* INTERFACE ROUTINES
*/
rel = (Relation) palloc(sizeof(RelationData));
MemSet((char *) rel, 0, sizeof(RelationData));
- rel->rd_fd = -1; /* table is not open */
- rel->rd_unlinked = true; /* table is not created yet */
+ rel->rd_fd = -1; /* physical file is not open */
RelationSetReferenceCount(rel, 1);
* have the storage manager create the relation.
* ----------------
*/
-
- /* smgrcreate() is moved to heap_storage_create() */
if (storage_create)
heap_storage_create(rel);
return rel;
}
-bool
+void
heap_storage_create(Relation rel)
{
- bool smgrcall = false;
-
- if (rel->rd_unlinked)
- {
- rel->rd_fd = (File) smgrcreate(DEFAULT_SMGR, rel);
- rel->rd_unlinked = false;
- smgrcall = true;
- }
- return smgrcall;
+ Assert(rel->rd_fd < 0);
+ rel->rd_fd = smgrcreate(DEFAULT_SMGR, rel);
+ Assert(rel->rd_fd >= 0);
}
/* ----------------------------------------------------------------
&entry);
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
+ {
index_drop(((Form_pg_index) GETSTRUCT(tuple))->indexrelid);
+ /* advance cmd counter to make catalog changes visible */
+ CommandCounterIncrement();
+ }
heap_endscan(scan);
heap_close(indexRelation, RowExclusiveLock);
LockRelation(currentIndex, AccessExclusiveLock);
/*
- * Release any buffers associated with this index. If they're
+ * Drop any buffers associated with this index. If they're
* dirty, they're just dropped without bothering to flush to disk.
*/
- ReleaseRelationBuffers(currentIndex);
+ DropRelationBuffers(currentIndex);
/* Now truncate the actual data and set blocks to zero */
smgrtruncate(DEFAULT_SMGR, currentIndex, 0);
/* ----------------
* TRUNCATE TABLE within a transaction block is dangerous, because
* if the transaction is later rolled back we have no way to
- * undo truncation of the relation's physical file. For now, allow it
- * but emit a warning message.
- * Someday we might want to consider postponing the physical truncate
- * 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.
+ * undo truncation of the relation's physical file. Disallow it
+ * except for a rel created in the current xact (which would be deleted
+ * on abort, anyway).
* ----------------
*/
if (IsTransactionBlock() && !rel->rd_myxactonly)
- elog(NOTICE, "Caution: TRUNCATE TABLE cannot be rolled back, so don't abort now");
+ elog(ERROR, "TRUNCATE TABLE cannot run inside a BEGIN/END block");
/*
* Release any buffers associated with this relation. If they're
* dirty, they're just dropped without bothering to flush to disk.
*/
-
- ReleaseRelationBuffers(rel);
+ DropRelationBuffers(rel);
/* Now truncate the actual data and set blocks to zero */
{
Relation rel;
Oid rid;
- bool istemp = (get_temp_rel_by_username(relname) != NULL);
bool has_toasttable;
+ bool istemp = (get_temp_rel_by_username(relname) != NULL);
+ int i;
/* ----------------
* Open and lock the relation.
*/
rel = heap_openr(relname, AccessExclusiveLock);
rid = RelationGetRelid(rel);
+ has_toasttable = rel->rd_rel->reltoastrelid != InvalidOid;
/* ----------------
* prevent deletion of system relations
/* allow temp of pg_class? Guess so. */
if (!istemp && !allow_system_table_mods &&
IsSystemRelationName(RelationGetRelationName(rel)))
- elog(ERROR, "System relation '%s' cannot be destroyed",
+ elog(ERROR, "System relation \"%s\" may not be dropped",
RelationGetRelationName(rel));
/* ----------------
- * DROP TABLE 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.
+ * Release all buffers that belong to this relation, after writing
+ * any that are dirty
* ----------------
*/
- if (IsTransactionBlock() && !rel->rd_myxactonly)
- elog(NOTICE, "Caution: DROP TABLE cannot be rolled back, so don't abort now");
+ i = FlushRelationBuffers(rel, (BlockNumber) 0);
+ if (i < 0)
+ elog(ERROR, "heap_drop_with_catalog: FlushRelationBuffers returned %d",
+ i);
/* ----------------
- * remove inheritance information
+ * remove rules if necessary
* ----------------
*/
- RelationRemoveInheritance(rel);
+ if (rel->rd_rules != NULL)
+ RelationRemoveRules(rid);
+
+ /* triggers */
+ RelationRemoveTriggers(rel);
/* ----------------
- * remove indexes if necessary
+ * remove inheritance information
* ----------------
*/
- /* should ignore relhasindex */
- RelationRemoveIndexes(rel);
+ RelationRemoveInheritance(rel);
/* ----------------
- * remove rules if necessary
+ * remove indexes if necessary
* ----------------
*/
- if (rel->rd_rules != NULL)
- RelationRemoveRules(rid);
-
- /* triggers */
- RelationRemoveTriggers(rel);
+ RelationRemoveIndexes(rel);
/* ----------------
* delete attribute tuples
*/
DeleteRelationTuple(rel);
- /*
- * release dirty buffers of this relation; don't bother to write them
- */
- ReleaseRelationBuffers(rel);
-
/* ----------------
* unlink the relation's physical file and finish up.
* ----------------
*/
- if (rel->rd_rel->relkind != RELKIND_VIEW && ! rel->rd_unlinked)
+ if (rel->rd_rel->relkind != RELKIND_VIEW)
smgrunlink(DEFAULT_SMGR, rel);
- rel->rd_unlinked = true;
-
- /*
- * Remember if there is a toast relation for below
- */
- has_toasttable = rel->rd_rel->reltoastrelid != InvalidOid;
/*
* Close relcache entry, but *keep* AccessExclusiveLock on the
*/
RelationForgetRelation(rid);
+ /* and from the temp-table map */
if (istemp)
remove_temp_rel_by_relid(rid);
*
*
* 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
#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"
#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.
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
*
*
* 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())
{
Relation attributeRelation;
HeapTuple tuple;
int16 attnum;
+ int i;
Assert(OidIsValid(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
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);
/*
* 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
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);
}
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)
{
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
{
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);
}
/* ----------------
* ----------------
*/
void
-UpdateStats(Oid relid, long reltuples, bool inplace)
+UpdateStats(Oid relid, long reltuples)
{
Relation whichRel;
Relation pg_class;
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);
/* ----------------
* ----------------
*/
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,
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);
}
/* ----------------
*/
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
{
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);
}
{
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);
}
}
}
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 */
* 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);
if (IndexesAreActive(relid, true))
{
if (!activate)
- setRelhasindexInplace(relid, false, true);
+ setRelhasindex(relid, false);
else
return false;
}
heap_endscan(scan);
heap_close(indexRelation, AccessShareLock);
if (reindexed)
- setRelhasindexInplace(relid, true, false);
+ setRelhasindex(relid, true);
SetReindexProcessing(old);
return reindexed;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.58 2000/07/14 22:17:42 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.59 2000/11/08 22:09:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "utils/builtins.h"
#include "utils/syscache.h"
-static Relation copy_heap(Oid OIDOldHeap);
-static void copy_index(Oid OIDOldIndex, Oid OIDNewHeap);
+static Oid copy_heap(Oid OIDOldHeap, char *NewName);
+static void copy_index(Oid OIDOldIndex, Oid OIDNewHeap, char *NewIndexName);
static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
/*
* cluster
*
- * Check that the relation is a relation in the appropriate user
- * ACL. I will use the same security that limits users on the
- * renamerel() function.
- *
- * Check that the index specified is appropriate for the task
- * ( ie it's an index over this relation ). This is trickier.
- *
+ * STILL TO DO:
* Create a list of all the other indicies on this relation. Because
* the cluster will wreck all the tids, I'll need to destroy bogus
* indicies. The user will have to re-create them. Not nice, but
* destroy re-build. This may be possible. I'll check out what the
* index create functiond want in the way of paramaters. On the other
* hand, re-creating n indicies may blow out the space.
- *
- * Create new (temporary) relations for the base heap and the new
- * index.
- *
- * Exclusively lock the relations.
- *
- * Create new clustered index and base heap relation.
- *
*/
void
cluster(char *oldrelname, char *oldindexname)
Oid OIDOldHeap,
OIDOldIndex,
OIDNewHeap;
-
Relation OldHeap,
OldIndex;
- Relation NewHeap;
-
- char NewIndexName[NAMEDATALEN];
+ HeapTuple tuple;
char NewHeapName[NAMEDATALEN];
+ char NewIndexName[NAMEDATALEN];
char saveoldrelname[NAMEDATALEN];
char saveoldindexname[NAMEDATALEN];
/*
- * Copy the arguments into local storage, because they are probably
- * in palloc'd storage that will go away when we commit a transaction.
+ * Copy the arguments into local storage, just to be safe.
*/
- strcpy(saveoldrelname, oldrelname);
- strcpy(saveoldindexname, oldindexname);
+ StrNCpy(saveoldrelname, oldrelname, NAMEDATALEN);
+ StrNCpy(saveoldindexname, oldindexname, NAMEDATALEN);
/*
- * Like vacuum, cluster spans transactions, so I'm going to handle it
- * in the same way: commit and restart transactions where needed.
- *
* We grab exclusive access to the target rel and index for the duration
- * of the initial transaction.
+ * of the transaction.
*/
-
OldHeap = heap_openr(saveoldrelname, AccessExclusiveLock);
OIDOldHeap = RelationGetRelid(OldHeap);
- OldIndex = index_openr(saveoldindexname); /* Open old index relation */
+ OldIndex = index_openr(saveoldindexname);
LockRelation(OldIndex, AccessExclusiveLock);
OIDOldIndex = RelationGetRelid(OldIndex);
/*
- * XXX Should check that index is in fact an index on this relation?
+ * Check that index is in fact an index on the given relation
*/
-
- heap_close(OldHeap, NoLock);/* do NOT give up the locks */
+ tuple = SearchSysCacheTuple(INDEXRELID,
+ ObjectIdGetDatum(OIDOldIndex),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "CLUSTER: no pg_index entry for index %u",
+ OIDOldIndex);
+ if (((Form_pg_index) GETSTRUCT(tuple))->indrelid != OIDOldHeap)
+ elog(ERROR, "CLUSTER: \"%s\" is not an index for table \"%s\"",
+ saveoldindexname, saveoldrelname);
+
+ /* Drop relcache refcnts, but do NOT give up the locks */
+ heap_close(OldHeap, NoLock);
index_close(OldIndex);
/*
- * I need to build the copies of the heap and the index. The Commit()
- * between here is *very* bogus. If someone is appending stuff, they
- * will get the lock after being blocked and add rows which won't be
- * present in the new table. Bleagh! I'd be best to try and ensure
- * that no-one's in the tables for the entire duration of this process
- * with a pg_vlock. XXX Isn't the above comment now invalid?
+ * Create the new heap with a temporary name.
*/
- NewHeap = copy_heap(OIDOldHeap);
- OIDNewHeap = RelationGetRelid(NewHeap);
- strcpy(NewHeapName, RelationGetRelationName(NewHeap));
+ snprintf(NewHeapName, NAMEDATALEN, "temp_%u", OIDOldHeap);
+
+ OIDNewHeap = copy_heap(OIDOldHeap, NewHeapName);
/* To make the new heap visible (which is until now empty). */
CommandCounterIncrement();
+ /*
+ * Copy the heap data into the new table in the desired order.
+ */
rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex);
- /* To flush the filled new heap (and the statistics about it). */
+ /* To make the new heap's data visible. */
CommandCounterIncrement();
/* Create new index over the tuples of the new heap. */
- copy_index(OIDOldIndex, OIDNewHeap);
- snprintf(NewIndexName, NAMEDATALEN, "temp_%x", OIDOldIndex);
+ snprintf(NewIndexName, NAMEDATALEN, "temp_%u", OIDOldIndex);
- /*
- * make this really happen. Flush all the buffers. (Believe me, it is
- * necessary ... ended up in a mess without it.)
- */
- CommitTransactionCommand();
- StartTransactionCommand();
+ copy_index(OIDOldIndex, OIDNewHeap, NewIndexName);
+
+ CommandCounterIncrement();
/* Destroy old heap (along with its index) and rename new. */
heap_drop_with_catalog(saveoldrelname, allowSystemTableMods);
- CommitTransactionCommand();
- StartTransactionCommand();
+ CommandCounterIncrement();
renamerel(NewHeapName, saveoldrelname);
+
+ /* This one might be unnecessary, but let's be safe. */
+ CommandCounterIncrement();
+
renamerel(NewIndexName, saveoldindexname);
}
-static Relation
-copy_heap(Oid OIDOldHeap)
+static Oid
+copy_heap(Oid OIDOldHeap, char *NewName)
{
- char NewName[NAMEDATALEN];
TupleDesc OldHeapDesc,
tupdesc;
Oid OIDNewHeap;
- Relation NewHeap,
- OldHeap;
-
- /*
- * Create a new heap relation with a temporary name, which has the
- * same tuple description as the old one.
- */
- snprintf(NewName, NAMEDATALEN, "temp_%x", OIDOldHeap);
+ Relation OldHeap;
OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
OldHeapDesc = RelationGetDescr(OldHeap);
* Need to make a copy of the tuple descriptor,
* heap_create_with_catalog modifies it.
*/
-
tupdesc = CreateTupleDescCopy(OldHeapDesc);
OIDNewHeap = heap_create_with_catalog(NewName, tupdesc,
allowSystemTableMods);
if (!OidIsValid(OIDNewHeap))
- elog(ERROR, "clusterheap: cannot create temporary heap relation\n");
+ elog(ERROR, "copy_heap: cannot create temporary heap relation");
- /* XXX why are we bothering to do this: */
- NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
-
- heap_close(NewHeap, AccessExclusiveLock);
- heap_close(OldHeap, AccessExclusiveLock);
+ heap_close(OldHeap, NoLock);
- return NewHeap;
+ return OIDNewHeap;
}
static void
-copy_index(Oid OIDOldIndex, Oid OIDNewHeap)
+copy_index(Oid OIDOldIndex, Oid OIDNewHeap, char *NewIndexName)
{
Relation OldIndex,
NewHeap;
Form_pg_index Old_pg_index_Form;
Form_pg_class Old_pg_index_relation_Form;
IndexInfo *indexInfo;
- char *NewIndexName;
NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
OldIndex = index_open(OIDOldIndex);
/*
- * OK. Create a new (temporary) index for the one that's already here.
+ * Create a new (temporary) index like the one that's already here.
* To do this I get the info from pg_index, and add a new index with
* a temporary name.
*/
Old_pg_index_Tuple = SearchSysCacheTupleCopy(INDEXRELID,
- ObjectIdGetDatum(RelationGetRelid(OldIndex)),
+ ObjectIdGetDatum(OIDOldIndex),
0, 0, 0);
Assert(Old_pg_index_Tuple);
Old_pg_index_Form = (Form_pg_index) GETSTRUCT(Old_pg_index_Tuple);
indexInfo = BuildIndexInfo(Old_pg_index_Tuple);
Old_pg_index_relation_Tuple = SearchSysCacheTupleCopy(RELOID,
- ObjectIdGetDatum(RelationGetRelid(OldIndex)),
+ ObjectIdGetDatum(OIDOldIndex),
0, 0, 0);
Assert(Old_pg_index_relation_Tuple);
Old_pg_index_relation_Form = (Form_pg_class) GETSTRUCT(Old_pg_index_relation_Tuple);
- /* Set the name. */
- NewIndexName = palloc(NAMEDATALEN); /* XXX */
- snprintf(NewIndexName, NAMEDATALEN, "temp_%x", OIDOldIndex);
-
index_create(RelationGetRelationName(NewHeap),
NewIndexName,
indexInfo,
Old_pg_index_Form->indisprimary,
allowSystemTableMods);
- setRelhasindexInplace(OIDNewHeap, true, false);
+ setRelhasindex(OIDNewHeap, true);
index_close(OldIndex);
- heap_close(NewHeap, AccessExclusiveLock);
+ heap_close(NewHeap, NoLock);
}
index_endscan(ScanDesc);
index_close(LocalOldIndex);
- heap_close(LocalOldHeap, AccessExclusiveLock);
- heap_close(LocalNewHeap, AccessExclusiveLock);
+ heap_close(LocalOldHeap, NoLock);
+ heap_close(LocalNewHeap, NoLock);
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.108 2000/10/26 21:34:44 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.109 2000/11/08 22:09:57 tgl Exp $
*
* NOTES
* The PerformAddAttribute() code, like most of the relation
/*
* Update toast rel's pg_class entry to show that it has an index.
- * NOTE this also does CommandCounterIncrement() to make index visible.
*/
- setRelhasindexInplace(toast_relid, true, false);
+ setRelhasindex(toast_relid, true);
+
+ /*
+ * Make index visible
+ */
+ CommandCounterIncrement();
/*
* Get the OID of the newly created index
attrtuple = SearchSysCacheTuple(ATTNAME, ObjectIdGetDatum(relation->rd_id),
PointerGetDatum(attrname), 0, 0);
if (!HeapTupleIsValid(attrtuple))
- {
elog(ERROR, "'%s' is not an attribute of class '%s'",
attrname, relname);
- }
oid = attrtuple->t_data->t_oid;
/*** Call CreateComments() to create/drop the comments ***/
/*** Now, close the heap relation and return ***/
- heap_close(relation, AccessShareLock);
-
+ heap_close(relation, NoLock);
}
/*------------------------------------------------------------------
heap_endscan(scan);
heap_close(pg_trigger, AccessShareLock);
- heap_close(relation, AccessShareLock);
-
+ heap_close(relation, NoLock);
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.64 2000/09/12 21:06:47 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.65 2000/11/08 22:09:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
StoreCatalogInheritance(relationId, inheritList);
+ /*
+ * We must bump the command counter to make the newly-created relation
+ * tuple visible for opening.
+ */
+ CommandCounterIncrement();
+
+ /*
+ * Open the new relation and acquire exclusive lock on it. This isn't
+ * really necessary for locking out other backends (since they can't
+ * see the new rel anyway until we commit), but it keeps the lock manager
+ * from complaining about deadlock risks.
+ */
+ rel = heap_openr(relname, AccessExclusiveLock);
+
/*
* Now add any newly specified column default values and CHECK
* constraints to the new relation. These are passed to us in the
rawDefaults = lappend(rawDefaults, rawEnt);
}
- /* If no raw defaults and no constraints, nothing to do. */
- if (rawDefaults == NIL && stmt->constraints == NIL)
- return;
-
- /*
- * We must bump the command counter to make the newly-created relation
- * tuple visible for opening.
- */
- CommandCounterIncrement();
-
- /*
- * Open the new relation.
- */
- rel = heap_openr(relname, AccessExclusiveLock);
-
/*
- * Parse and add the defaults/constraints.
+ * Parse and add the defaults/constraints, if any.
*/
- AddRelationRawConstraints(rel, rawDefaults, stmt->constraints);
+ if (rawDefaults || stmt->constraints)
+ AddRelationRawConstraints(rel, rawDefaults, stmt->constraints);
/*
* Clean up. We keep lock on new relation (although it shouldn't be
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.39 2000/10/22 23:32:39 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.40 2000/11/08 22:09:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* backends to flush their relcache entries and in particular their
* cached lists of the indexes for this relation.
*/
- setRelhasindexInplace(relationId, true, false);
+ setRelhasindex(relationId, true);
}
{
HeapTuple tuple;
+ /* ----------------
+ * 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");
+
tuple = SearchSysCacheTuple(RELNAME,
PointerGetDatum(name),
0, 0, 0);
{
HeapTuple tuple;
+ /* ----------------
+ * 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");
+
tuple = SearchSysCacheTuple(RELNAME,
PointerGetDatum(name),
0, 0, 0);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.51 2000/10/22 23:32:39 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.52 2000/11/08 22:09:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Oid reloid;
char relkind;
Relation irelations[Num_pg_class_indices];
-#ifdef OLD_FILE_NAMING
- int i;
- char oldpath[MAXPGPATH],
- newpath[MAXPGPATH],
- toldpath[MAXPGPATH + 10],
- tnewpath[MAXPGPATH + 10];
-#endif
if (!allowSystemTableMods && IsSystemRelationName(oldrelname))
- elog(ERROR, "renamerel: system relation \"%s\" not renamed",
+ elog(ERROR, "renamerel: system relation \"%s\" may not be renamed",
oldrelname);
if (!allowSystemTableMods && IsSystemRelationName(newrelname))
/*
* Check for renaming a temp table, which only requires altering
- * the temp-table mapping, not the physical table.
+ * the temp-table mapping, not the underlying table.
*/
if (rename_temp_relation(oldrelname, newrelname))
return; /* all done... */
targetrelation = RelationNameGetRelation(oldrelname);
if (!RelationIsValid(targetrelation))
- elog(ERROR, "Relation '%s' does not exist", oldrelname);
+ elog(ERROR, "Relation \"%s\" does not exist", oldrelname);
/*
* Grab an exclusive lock on the target table, which we will NOT
*/
LockRelation(targetrelation, AccessExclusiveLock);
- /* ----------------
- * RENAME TABLE within a transaction block is dangerous, because
- * if the transaction is later rolled back we have no way to
- * undo the rename of the relation's physical file. For now, allow it
- * but emit a warning message.
- * Someday we might want to consider postponing the physical rename
- * 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.
- * ----------------
- */
- if (IsTransactionBlock() && !targetrelation->rd_myxactonly)
- elog(NOTICE, "Caution: RENAME TABLE cannot be rolled back, so don't abort now");
-
reloid = RelationGetRelid(targetrelation);
relkind = targetrelation->rd_rel->relkind;
- /*
- * Flush all blocks of the relation out of the buffer pool. We need
- * this because the blocks are marked with the relation's name as well
- * as OID. If some backend tries to write a dirty buffer with
- * mdblindwrt after we've renamed the physical file, we'll be in big
- * trouble.
- *
- * Since we hold the exclusive lock on the relation, we don't have to
- * worry about more blocks being read in while we finish the rename.
- */
- if (FlushRelationBuffers(targetrelation, (BlockNumber) 0) < 0)
- elog(ERROR, "renamerel: unable to flush relation from buffer pool");
-
- /*
- * Make sure smgr and lower levels close the relation's files. (Next
- * access to rel will reopen them.)
- *
- * Note: we rely on shared cache invalidation message to make other
- * backends close and re-open the files.
- */
- smgrclose(DEFAULT_SMGR, targetrelation);
-
/*
* Close rel, but keep exclusive lock!
*/
* the right instant). It'll get rebuilt on next access to relation.
*
* XXX What if relation is myxactonly?
+ *
+ * XXX this is probably not necessary anymore?
*/
- targetrelation = NULL; /* make sure I don't touch it again */
RelationIdInvalidateRelationCacheByRelationId(reloid);
/*
elog(ERROR, "renamerel: relation \"%s\" exists", newrelname);
/*
- * Update pg_class tuple with new relname.
+ * Update pg_class tuple with new relname. (Scribbling on oldreltup
+ * is OK because it's a copy...)
*/
StrNCpy(NameStr(((Form_pg_class) GETSTRUCT(oldreltup))->relname),
newrelname, NAMEDATALEN);
*/
if (relkind != RELKIND_INDEX)
TypeRename(oldrelname, newrelname);
-
-#ifdef OLD_FILE_NAMING
- /*
- * Perform physical rename of files. If this fails, we haven't yet
- * done anything irreversible. NOTE that this MUST be the last step;
- * an error occurring afterwards would leave the relation hosed!
- *
- * XXX smgr.c ought to provide an interface for this; doing it directly
- * is bletcherous.
- */
- strcpy(oldpath, relpath(oldrelname));
- strcpy(newpath, relpath(newrelname));
- if (rename(oldpath, newpath) < 0)
- elog(ERROR, "renamerel: unable to rename %s to %s: %m",
- oldpath, newpath);
-
- /* rename additional segments of relation, too */
- for (i = 1;; i++)
- {
- sprintf(toldpath, "%s.%d", oldpath, i);
- sprintf(tnewpath, "%s.%d", newpath, i);
- if (rename(toldpath, tnewpath) < 0)
- {
- /* expected case is that there's not another segment file */
- if (errno == ENOENT)
- break;
- /* otherwise we're up the creek... */
- elog(ERROR, "renamerel: unable to rename %s to %s: %m",
- toldpath, tnewpath);
- }
- }
-#endif
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.78 2000/10/16 17:08:05 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.79 2000/11/08 22:09:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
HeapScanDesc tgscan;
ScanKeyData key;
HeapTuple tup;
+ bool found = false;
tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
DeleteComments(tup->t_data->t_oid);
heap_delete(tgrel, &tup->t_self, NULL);
+
+ found = true;
}
heap_endscan(tgscan);
/* ----------
- * Need to bump it here so the following doesn't see
- * the already deleted triggers again for a self-referencing
- * table.
+ * If we deleted any triggers, must update pg_class entry and
+ * advance command counter to make the updated entry visible.
+ * This is fairly annoying, since we'e just going to drop the
+ * durn thing later, but it's necessary to have a consistent
+ * state in case we do CommandCounterIncrement() below ---
+ * if RelationBuildTriggers() runs, it will complain otherwise.
+ * Perhaps RelationBuildTriggers() shouldn't be so picky...
* ----------
*/
- CommandCounterIncrement();
+ if (found)
+ {
+ Relation pgrel;
+ Relation ridescs[Num_pg_class_indices];
+
+ pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
+ tup = SearchSysCacheTupleCopy(RELOID,
+ RelationGetRelid(rel),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "RelationRemoveTriggers: relation %u not found in pg_class",
+ RelationGetRelid(rel));
+
+ ((Form_pg_class) GETSTRUCT(tup))->reltriggers = 0;
+ heap_update(pgrel, &tup->t_self, tup, NULL);
+ CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
+ CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tup);
+ CatalogCloseIndices(Num_pg_class_indices, ridescs);
+ heap_freetuple(tup);
+ heap_close(pgrel, RowExclusiveLock);
+ CommandCounterIncrement();
+ }
/* ----------
* Also drop all constraint triggers referencing this relation
pg_trigger = (Form_pg_trigger) GETSTRUCT(tup);
- refrel = heap_open(pg_trigger->tgrelid, NoLock);
+ stmt.trigname = pstrdup(NameStr(pg_trigger->tgname));
+
+ /* May as well grab AccessExclusiveLock, since DropTrigger will. */
+ refrel = heap_open(pg_trigger->tgrelid, AccessExclusiveLock);
stmt.relname = pstrdup(RelationGetRelationName(refrel));
heap_close(refrel, NoLock);
- stmt.trigname = DatumGetCString(DirectFunctionCall1(nameout,
- NameGetDatum(&pg_trigger->tgname)));
-
-
elog(NOTICE, "DROP TABLE implicitly drops referential integrity trigger from table \"%s\"", stmt.relname);
DropTrigger(&stmt);
/* ----------
* Need to do a command counter increment here to show up
- * new pg_class.reltriggers in the next loop invocation already
- * (there are multiple referential integrity action
+ * new pg_class.reltriggers in the next loop iteration
+ * (in case there are multiple referential integrity action
* triggers for the same FK table defined on the PK table).
* ----------
*/
* We need not examine the "index" data, just the trigger array
* itself; if we have the same triggers with the same types, the
* derived index data should match.
- *
- * XXX It seems possible that the same triggers could appear in different
- * orders in the two trigger arrays; do we need to handle that?
*/
if (trigdesc1 != NULL)
{
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: execAmi.c,v 1.54 2000/10/26 21:35:15 tgl Exp $
+ * $Id: execAmi.c,v 1.55 2000/11/08 22:09:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* nkeys -- number of keys
* skeys -- keys to restrict scanning
* isindex -- if this is true, the relation is the relid of
- * an index relation, else it is an index into the
- * range table.
+ * an index relation, else it is a heap relation.
* Returns the relation as(relDesc scanDesc)
- * If this structure is changed, need to modify the access macros
- * defined in execInt.h.
* ----------------------------------------------------------------
*/
void
*/
/* ----------------
- * open the relation with the correct call depending
+ * Open the relation with the correct call depending
* on whether this is a heap relation or an index relation.
*
- * Do not lock the rel here; beginscan will acquire AccessShareLock.
+ * For a table, acquire AccessShareLock for the duration of the query
+ * execution. For indexes, acquire no lock here; the index machinery
+ * does its own locks and unlocks. (We rely on having some kind of
+ * lock on the parent table to ensure the index won't go away!)
* ----------------
*/
if (isindex)
relation = index_open(relOid);
else
- relation = heap_open(relOid, NoLock);
+ relation = heap_open(relOid, AccessShareLock);
scanDesc = ExecBeginScan(relation,
nkeys,
{
Pointer scanDesc;
- scanDesc = NULL;
-
/* ----------------
* open the appropriate type of scan.
*
HeapScanDesc scanDesc;
/* ----------------
- * shut down the heap scan and close the heap relation
+ * get state for node and shut down the heap scan, if any
* ----------------
*/
switch (nodeTag(node))
{
-
case T_SeqScan:
state = ((SeqScan *) node)->scanstate;
break;
if (scanDesc != NULL)
heap_endscan(scanDesc);
- /*
- * endscan released AccessShareLock acquired by beginscan. If we are
- * holding any stronger locks on the rel, they should be held till end
- * of xact. Therefore, we need only close the rel and not release
- * locks.
- */
- if (relation != NULL)
- heap_close(relation, NoLock);
-
/* ----------------
* if this is an index scan then we have to take care
- * of the index relations as well..
+ * of the index relations as well.
* ----------------
*/
if (IsA(node, IndexScan))
for (i = 0; i < numIndices; i++)
{
/* ----------------
- * shut down each of the scans and
+ * shut down each of the index scans and
* close each of the index relations
* ----------------
*/
index_close(indexRelationDescs[i]);
}
}
+
+ /*
+ * Finally, close the heap relation.
+ *
+ * Currently, we do not release the AccessShareLock acquired by
+ * ExecOpenScanR. This lock should be held till end of transaction.
+ * (There is a faction that considers this too much locking, however.)
+ */
+ if (relation != NULL)
+ heap_close(relation, NoLock);
}
/* ----------------------------------------------------------------
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: analyze.c,v 1.164 2000/11/05 01:42:07 tgl Exp $
+ * $Id: analyze.c,v 1.165 2000/11/08 22:09:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
release_pstate_resources(ParseState *pstate)
{
if (pstate->p_target_relation != NULL)
- heap_close(pstate->p_target_relation, AccessShareLock);
+ heap_close(pstate->p_target_relation, NoLock);
pstate->p_target_relation = NULL;
pstate->p_target_rangetblentry = NULL;
}
qry->commandType = CMD_DELETE;
/* set up a range table */
+ lockTargetTable(pstate, stmt->relname);
makeRangeTable(pstate, NIL);
setTargetTable(pstate, stmt->relname, stmt->inh, true);
qry->commandType = CMD_INSERT;
pstate->p_is_insert = true;
+ /*
+ * Must get write lock on target table before scanning SELECT,
+ * else we will grab the wrong kind of initial lock if the target
+ * table is also mentioned in the SELECT part.
+ */
+ lockTargetTable(pstate, stmt->relname);
+
/*
* Is it INSERT ... SELECT or INSERT ... VALUES?
*/
qry->commandType = CMD_UTILITY;
qry->utilityStmt = (Node *) stmt;
+ /*
+ * To avoid deadlock, make sure the first thing we do is grab
+ * AccessExclusiveLock on the target relation. This will be
+ * needed by DefineQueryRewrite(), and we don't want to grab a lesser
+ * lock beforehand. We don't need to hold a refcount on the relcache
+ * entry, however.
+ */
+ heap_close(heap_openr(stmt->object->relname, AccessExclusiveLock),
+ NoLock);
+
/*
* NOTE: 'OLD' must always have a varno equal to 1 and 'NEW'
* equal to 2. Set up their RTEs in the main pstate for use
qry->isBinary = FALSE;
}
+ /* make FOR UPDATE clause available to addRangeTableEntry */
+ pstate->p_forUpdate = stmt->forUpdate;
+
/* set up a range table */
makeRangeTable(pstate, stmt->fromClause);
qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
- if (stmt->forUpdate != NULL)
+ if (stmt->forUpdate != NIL)
transformForUpdate(qry, stmt->forUpdate);
return qry;
qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
- if (forUpdate != NULL)
+ if (forUpdate != NIL)
transformForUpdate(qry, forUpdate);
return qry;
* used in FROM, we'd fail to notice that it should be marked
* checkForRead as well as checkForWrite. See setTargetTable().
*/
+ lockTargetTable(pstate, stmt->relname);
makeRangeTable(pstate, stmt->fromClause);
setTargetTable(pstate, stmt->relname, stmt->inh, true);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.207 2000/11/08 21:28:06 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.208 2000/11/08 22:09:58 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
* when some sort of pg_privileges relation is introduced.
* - Todd A. Brandys 1998-01-01?
*/
-%token ABORT_TRANS, ACCESS, AFTER, AGGREGATE, ANALYZE, ANALYSE /* British */
+%token ABORT_TRANS, ACCESS, AFTER, AGGREGATE, ANALYZE, ANALYSE,
BACKWARD, BEFORE, BINARY, BIT,
CACHE, CHECKPOINT, CLUSTER, COMMENT, COPY, CREATEDB, CREATEUSER, CYCLE,
DATABASE, DELIMITERS, DO,
/* NOT USED
RecipeStmt: EXECUTE RECIPE recipe_name
{
- RecipeStmt *n;
- if (!IsTransactionBlock())
- elog(ERROR,"EXECUTE RECIPE may only be used in begin/end transaction blocks");
-
- n = makeNode(RecipeStmt);
+ RecipeStmt *n = makeNode(RecipeStmt);
n->recipeName = $3;
$$ = (Node *)n;
}
ReindexStmt: REINDEX reindex_type name opt_force
{
ReindexStmt *n = makeNode(ReindexStmt);
- if (IsTransactionBlock())
- elog(ERROR,"REINDEX command could only be used outside begin/end transaction blocks");
n->reindexType = $2;
n->name = $3;
n->force = $4;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.70 2000/10/07 00:58:17 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.71 2000/11/08 22:09:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
}
+/*
+ * lockTargetTable
+ * Find the target relation of INSERT/UPDATE/DELETE and acquire write
+ * lock on it. This must be done before building the range table,
+ * in case the target is also mentioned as a source relation --- we
+ * want to be sure to grab the write lock before any read lock.
+ *
+ * The ParseState's link to the target relcache entry is also set here.
+ */
+void
+lockTargetTable(ParseState *pstate, char *relname)
+{
+ /* Close old target; this could only happen for multi-action rules */
+ if (pstate->p_target_relation != NULL)
+ heap_close(pstate->p_target_relation, NoLock);
+ pstate->p_target_relation = NULL;
+ pstate->p_target_rangetblentry = NULL; /* setTargetTable will set this */
+
+ /*
+ * Open target rel and grab suitable lock (which we will hold till
+ * end of transaction).
+ *
+ * analyze.c will eventually do the corresponding heap_close(),
+ * but *not* release the lock.
+ */
+ pstate->p_target_relation = heap_openr(relname, RowExclusiveLock);
+}
+
/*
* setTargetTable
* Add the target relation of INSERT/UPDATE/DELETE to the range table,
if (inJoinSet)
addRTEtoJoinList(pstate, rte);
- /* This could only happen for multi-action rules */
- if (pstate->p_target_relation != NULL)
- heap_close(pstate->p_target_relation, AccessShareLock);
+ /* lockTargetTable should have been called earlier */
+ Assert(pstate->p_target_relation != NULL);
pstate->p_target_rangetblentry = rte;
- pstate->p_target_relation = heap_open(rte->relid, AccessShareLock);
- /* will close relation later, see analyze.c */
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.49 2000/09/29 18:21:36 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.50 2000/11/08 22:09:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
char *colname);
static Node *scanJoinForColumn(JoinExpr *join, char *colname,
int sublevels_up);
+static bool isForUpdate(ParseState *pstate, char *relname);
static List *expandNamesVars(ParseState *pstate, List *names, List *vars);
static void warnAutoRange(ParseState *pstate, char *refname);
bool inFromCl)
{
char *refname = alias ? alias->relname : relname;
+ LOCKMODE lockmode;
Relation rel;
RangeTblEntry *rte;
Attr *eref;
/*
* Get the rel's OID. This access also ensures that we have an
- * up-to-date relcache entry for the rel. We don't need to keep it
- * open, however. Since this is open anyway, let's check that the
- * number of column aliases is reasonable. - Thomas 2000-02-04
+ * up-to-date relcache entry for the rel. Since this is typically
+ * the first access to a rel in a statement, be careful to get the
+ * right access level depending on whether we're doing SELECT FOR UPDATE.
*/
- rel = heap_openr(relname, AccessShareLock);
+ lockmode = isForUpdate(pstate, relname) ? RowShareLock : AccessShareLock;
+ rel = heap_openr(relname, lockmode);
rte->relid = RelationGetRelid(rel);
- maxattrs = RelationGetNumberOfAttributes(rel);
eref = alias ? (Attr *) copyObject(alias) : makeAttr(refname, NULL);
numaliases = length(eref->attrs);
+ /*
+ * Since the rel is open anyway, let's check that the
+ * number of column aliases is reasonable. - Thomas 2000-02-04
+ */
+ maxattrs = RelationGetNumberOfAttributes(rel);
if (maxattrs < numaliases)
elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
refname, maxattrs, numaliases);
}
rte->eref = eref;
- heap_close(rel, AccessShareLock);
+ /*
+ * Drop the rel refcount, but keep the access lock till end of transaction
+ * so that the table can't be deleted or have its schema modified
+ * underneath us.
+ */
+ heap_close(rel, NoLock);
/*----------
* Flags:
return rte;
}
+/*
+ * Has the specified relname been selected FOR UPDATE?
+ */
+static bool
+isForUpdate(ParseState *pstate, char *relname)
+{
+ /* Outer loop to check parent query levels as well as this one */
+ while (pstate != NULL)
+ {
+ if (pstate->p_forUpdate != NIL)
+ {
+ if (lfirst(pstate->p_forUpdate) == NULL)
+ {
+ /* all tables used in query */
+ return true;
+ }
+ else
+ {
+ /* just the named tables */
+ List *l;
+
+ foreach(l, pstate->p_forUpdate)
+ {
+ char *rname = lfirst(l);
+
+ if (strcmp(relname, rname) == 0)
+ return true;
+ }
+ }
+ }
+ pstate = pstate->parentParseState;
+ }
+ return false;
+}
+
/*
* Add the given RTE as a top-level entry in the pstate's join list,
* unless there already is an entry for it.
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.82 2000/10/05 19:11:34 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.83 2000/11/08 22:09:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
fireRIRrules(Query *parsetree)
{
int rt_index;
- RangeTblEntry *rte;
- Relation rel;
- List *locks;
- RuleLock *rules;
- RewriteRule *rule;
- bool relIsUsed;
- int i;
- List *l;
/*
* don't try to convert this into a foreach loop, because rtable list
rt_index = 0;
while (rt_index < length(parsetree->rtable))
{
+ RangeTblEntry *rte;
+ Relation rel;
+ List *locks;
+ RuleLock *rules;
+ RewriteRule *rule;
+ LOCKMODE lockmode;
+ bool relIsUsed;
+ int i;
+ List *l;
+
++rt_index;
rte = rt_fetch(rt_index, parsetree->rtable);
if (!relIsUsed && rt_index != parsetree->resultRelation)
continue;
- rel = heap_openr(rte->relname, AccessShareLock);
+ /*
+ * This may well be the first access to the relation during
+ * the current statement (it will be, if this Query was extracted
+ * from a rule or somehow got here other than via the parser).
+ * Therefore, grab the appropriate lock type for the relation,
+ * and do not release it until end of transaction. This protects
+ * the rewriter and planner against schema changes mid-query.
+ *
+ * If the relation is the query's result relation, then RewriteQuery()
+ * already got the right lock on it, so we need no additional lock.
+ * Otherwise, check to see if the relation is accessed FOR UPDATE
+ * or not.
+ */
+ if (rt_index == parsetree->resultRelation)
+ lockmode = NoLock;
+ else if (intMember(rt_index, parsetree->rowMarks))
+ lockmode = RowShareLock;
+ else
+ lockmode = AccessShareLock;
+
+ rel = heap_openr(rte->relname, lockmode);
+
rules = rel->rd_rules;
if (rules == NULL)
{
- heap_close(rel, AccessShareLock);
+ heap_close(rel, NoLock);
continue;
}
relIsUsed);
}
- heap_close(rel, AccessShareLock);
+ heap_close(rel, NoLock);
}
/*
* the statement is an update, insert or delete - fire rules on it.
*/
result_relation = parsetree->resultRelation;
+ Assert(result_relation != 0);
rt_entry = rt_fetch(result_relation, parsetree->rtable);
- rt_entry_relation = heap_openr(rt_entry->relname, AccessShareLock);
+
+ /*
+ * This may well be the first access to the result relation during
+ * the current statement (it will be, if this Query was extracted
+ * from a rule or somehow got here other than via the parser).
+ * Therefore, grab the appropriate lock type for a result relation,
+ * and do not release it until end of transaction. This protects the
+ * rewriter and planner against schema changes mid-query.
+ */
+ rt_entry_relation = heap_openr(rt_entry->relname, RowExclusiveLock);
+
rt_entry_locks = rt_entry_relation->rd_rules;
if (rt_entry_locks != NULL)
qual_products);
}
- heap_close(rt_entry_relation, AccessShareLock);
+ heap_close(rt_entry_relation, NoLock); /* keep lock! */
return product_queries;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.92 2000/10/28 16:20:55 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.93 2000/11/08 22:09:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#endif
/*
- * FlushBuffer -- like WriteBuffer, but force the page to disk.
+ * FlushBuffer -- like WriteBuffer, but write the page immediately,
+ * rather than just marking it dirty. On success return, the buffer will
+ * no longer be dirty.
*
* 'buffer' is known to be dirty/pinned, so there should not be a
* problem reading the BufferDesc members without the BufMgrLock
* (nobody should be able to change tags out from under us).
*
- * Unpin if 'release' is TRUE.
+ * If 'sync' is true, a synchronous write is wanted (wait for buffer to hit
+ * the disk). Otherwise it's sufficient to issue the kernel write call.
+ *
+ * Unpin buffer if 'release' is true.
*/
int
-FlushBuffer(Buffer buffer, bool release)
+FlushBuffer(Buffer buffer, bool sync, bool release)
{
BufferDesc *bufHdr;
Relation bufrel;
int status;
if (BufferIsLocal(buffer))
- return FlushLocalBuffer(buffer, release) ? STATUS_OK : STATUS_ERROR;
+ return FlushLocalBuffer(buffer, sync, release) ? STATUS_OK : STATUS_ERROR;
if (BAD_BUFFER_ID(buffer))
return STATUS_ERROR;
*/
LockBuffer(BufferDescriptorGetBuffer(bufHdr), BUFFER_LOCK_SHARE);
- status = smgrflush(DEFAULT_SMGR, bufrel, bufHdr->tag.blockNum,
- (char *) MAKE_PTR(bufHdr->data));
+ if (sync)
+ status = smgrflush(DEFAULT_SMGR, bufrel, bufHdr->tag.blockNum,
+ (char *) MAKE_PTR(bufHdr->data));
+ else
+ status = smgrwrite(DEFAULT_SMGR, bufrel, bufHdr->tag.blockNum,
+ (char *) MAKE_PTR(bufHdr->data));
LockBuffer(BufferDescriptorGetBuffer(bufHdr), BUFFER_LOCK_UNLOCK);
- /* drop relcache refcnt incremented by RelationIdCacheGetRelation */
+ /* drop relcache refcnt incremented by RelationNodeCacheGetRelation */
RelationDecrementReferenceCount(bufrel);
if (status == SM_FAIL)
/*
* drop relcache refcnt incremented by
- * RelationIdCacheGetRelation
+ * RelationNodeCacheGetRelation
*/
RelationDecrementReferenceCount(reln);
}
bufHdr->flags &= ~BM_DIRTY;
}
- /* drop refcnt obtained by RelationIdCacheGetRelation */
+ /* drop refcnt obtained by RelationNodeCacheGetRelation */
if (reln != (Relation) NULL)
RelationDecrementReferenceCount(reln);
}
/*
* drop relcache refcnt incremented by
- * RelationIdCacheGetRelation
+ * RelationNodeCacheGetRelation
*/
RelationDecrementReferenceCount(reln);
SpinAcquire(BufMgrLock);
- /* drop relcache refcnt incremented by RelationIdCacheGetRelation */
+ /* drop relcache refcnt incremented by RelationNodeCacheGetRelation */
if (reln != (Relation) NULL)
RelationDecrementReferenceCount(reln);
}
/* ---------------------------------------------------------------------
- * ReleaseRelationBuffers
+ * DropRelationBuffers
*
* This function removes all the buffered pages for a relation
* from the buffer pool. Dirty pages are simply dropped, without
- * bothering to write them out first. This is used when the
- * relation is about to be deleted. We assume that the caller
- * holds an exclusive lock on the relation, which should assure
- * that no new buffers will be acquired for the rel meanwhile.
+ * bothering to write them out first. This is NOT rollback-able,
+ * and so should be used only with extreme caution!
+ *
+ * We assume that the caller holds an exclusive lock on the relation,
+ * which should assure that no new buffers will be acquired for the rel
+ * meanwhile.
*
* XXX currently it sequentially searches the buffer pool, should be
* changed to more clever ways of searching.
* --------------------------------------------------------------------
*/
void
-ReleaseRelationBuffers(Relation rel)
+DropRelationBuffers(Relation rel)
{
int i;
BufferDesc *bufHdr;
* this rel, since we hold exclusive lock on this rel.
*/
if (RelFileNodeEquals(rel->rd_node,
- BufferTagLastDirtied[i - 1].rnode))
+ BufferTagLastDirtied[i - 1].rnode))
+ BufferDirtiedByMe[i - 1] = false;
+ }
+
+ SpinRelease(BufMgrLock);
+}
+
+/* ---------------------------------------------------------------------
+ * DropRelFileNodeBuffers
+ *
+ * This is the same as DropRelationBuffers, except that the target
+ * relation is specified by RelFileNode.
+ *
+ * This is NOT rollback-able. One legitimate use is to clear the
+ * buffer cache of buffers for a relation that is being deleted
+ * during transaction abort.
+ * --------------------------------------------------------------------
+ */
+void
+DropRelFileNodeBuffers(RelFileNode rnode)
+{
+ int i;
+ BufferDesc *bufHdr;
+
+ /* We have to search both local and shared buffers... */
+
+ for (i = 0; i < NLocBuffer; i++)
+ {
+ bufHdr = &LocalBufferDescriptors[i];
+ if (RelFileNodeEquals(bufHdr->tag.rnode, rnode))
+ {
+ bufHdr->flags &= ~(BM_DIRTY | BM_JUST_DIRTIED);
+ LocalRefCount[i] = 0;
+ bufHdr->tag.rnode.relNode = InvalidOid;
+ }
+ }
+
+ SpinAcquire(BufMgrLock);
+ for (i = 1; i <= NBuffers; i++)
+ {
+ bufHdr = &BufferDescriptors[i - 1];
+recheck:
+ if (RelFileNodeEquals(bufHdr->tag.rnode, rnode))
+ {
+
+ /*
+ * If there is I/O in progress, better wait till it's done;
+ * don't want to delete the relation out from under someone
+ * who's just trying to flush the buffer!
+ */
+ if (bufHdr->flags & BM_IO_IN_PROGRESS)
+ {
+ WaitIO(bufHdr, BufMgrLock);
+
+ /*
+ * By now, the buffer very possibly belongs to some other
+ * rel, so check again before proceeding.
+ */
+ goto recheck;
+ }
+ /* Now we can do what we came for */
+ bufHdr->flags &= ~(BM_DIRTY | BM_JUST_DIRTIED);
+
+ /*
+ * Release any refcount we may have.
+ *
+ * This is very probably dead code, and if it isn't then it's
+ * probably wrong. I added the Assert to find out --- tgl
+ * 11/99.
+ */
+ if (!(bufHdr->flags & BM_FREE))
+ {
+ /* Assert checks that buffer will actually get freed! */
+ Assert(PrivateRefCount[i - 1] == 1 &&
+ bufHdr->refcount == 1);
+ /* ReleaseBuffer expects we do not hold the lock at entry */
+ SpinRelease(BufMgrLock);
+ ReleaseBuffer(i);
+ SpinAcquire(BufMgrLock);
+ }
+ /*
+ * And mark the buffer as no longer occupied by this rel.
+ */
+ BufTableDelete(bufHdr);
+ }
+
+ /*
+ * Also check to see if BufferDirtiedByMe info for this buffer
+ * refers to the target relation, and clear it if so. This is
+ * independent of whether the current contents of the buffer
+ * belong to the target relation!
+ *
+ * NOTE: we have no way to clear BufferDirtiedByMe info in other
+ * backends, but hopefully there are none with that bit set for
+ * this rel, since we hold exclusive lock on this rel.
+ */
+ if (RelFileNodeEquals(rnode,
+ BufferTagLastDirtied[i - 1].rnode))
BufferDirtiedByMe[i - 1] = false;
}
* bothering to write them out first. This is used when we destroy a
* database, to avoid trying to flush data to disk when the directory
* tree no longer exists. Implementation is pretty similar to
- * ReleaseRelationBuffers() which is for destroying just one relation.
+ * DropRelationBuffers() which is for destroying just one relation.
* --------------------------------------------------------------------
*/
void
/* ---------------------------------------------------------------------
* FlushRelationBuffers
*
- * This function flushes all dirty pages of a relation out to disk.
+ * This function writes all dirty pages of a relation out to disk.
* Furthermore, pages that have blocknumber >= firstDelBlock are
* actually removed from the buffer pool. An error code is returned
* if we fail to dump a dirty buffer or if we find one of
* the target pages is pinned into the cache.
*
- * This is used by VACUUM before truncating the relation to the given
- * number of blocks. (TRUNCATE TABLE also uses it in the same way.)
- * It might seem unnecessary to flush dirty pages before firstDelBlock,
- * since VACUUM should already have committed its changes. However,
- * it is possible for there still to be dirty pages: if some page
- * had unwritten on-row tuple status updates from a prior transaction,
- * and VACUUM had no additional changes to make to that page, then
- * VACUUM won't have written it. This is harmless in most cases but
- * will break pg_upgrade, which relies on VACUUM to ensure that *all*
- * tuples have correct on-row status. So, we check and flush all
- * dirty pages of the rel regardless of block number.
- *
- * This is also used by RENAME TABLE (with firstDelBlock = 0)
- * to clear out the buffer cache before renaming the physical files of
- * a relation. Without that, some other backend might try to do a
- * blind write of a buffer page (relying on the BlindId of the buffer)
- * and fail because it's not got the right filename anymore.
+ * This is called by DROP TABLE to clear buffers for the relation
+ * from the buffer pool. Note that we must write dirty buffers,
+ * rather than just dropping the changes, because our transaction
+ * might abort later on; we want to roll back safely in that case.
+ *
+ * This is also called by VACUUM before truncating the relation to the
+ * given number of blocks. It might seem unnecessary for VACUUM to
+ * write dirty pages before firstDelBlock, since VACUUM should already
+ * have committed its changes. However, it is possible for there still
+ * to be dirty pages: if some page had unwritten on-row tuple status
+ * updates from a prior transaction, and VACUUM had no additional
+ * changes to make to that page, then VACUUM won't have written it.
+ * This is harmless in most cases but will break pg_upgrade, which
+ * relies on VACUUM to ensure that *all* tuples have correct on-row
+ * status. So, we check and flush all dirty pages of the rel
+ * regardless of block number.
*
* In all cases, the caller should be holding AccessExclusiveLock on
* the target relation to ensure that no other backend is busy reading
- * more blocks of the relation.
+ * more blocks of the relation (or might do so before we commit).
*
* Formerly, we considered it an error condition if we found dirty
* buffers here. However, since BufferSync no longer forces out all
{
if (bufHdr->flags & BM_DIRTY)
{
- if (FlushBuffer(-i - 1, false) != STATUS_OK)
+ if (FlushBuffer(-i - 1, false, false) != STATUS_OK)
{
elog(NOTICE, "FlushRelationBuffers(%s (local), %u): block %u is dirty, could not flush it",
RelationGetRelationName(rel), firstDelBlock,
for (i = 0; i < NBuffers; i++)
{
bufHdr = &BufferDescriptors[i];
-recheck:
if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node))
{
if (bufHdr->flags & BM_DIRTY)
{
PinBuffer(bufHdr);
SpinRelease(BufMgrLock);
- if (FlushBuffer(i + 1, true) != STATUS_OK)
+ if (FlushBuffer(i + 1, false, false) != STATUS_OK)
{
+ SpinAcquire(BufMgrLock);
+ UnpinBuffer(bufHdr);
+ SpinRelease(BufMgrLock);
elog(NOTICE, "FlushRelationBuffers(%s, %u): block %u is dirty (private %ld, global %d), could not flush it",
RelationGetRelationName(rel), firstDelBlock,
bufHdr->tag.blockNum,
return -1;
}
SpinAcquire(BufMgrLock);
-
- /*
- * Buffer could already be reassigned, so must recheck
- * whether it still belongs to rel before freeing it!
- */
- goto recheck;
+ UnpinBuffer(bufHdr);
}
if (!(bufHdr->flags & BM_FREE))
{
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/buffer/localbuf.c,v 1.33 2000/10/28 16:20:56 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/buffer/localbuf.c,v 1.34 2000/11/08 22:09:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* flushes a local buffer
*/
int
-FlushLocalBuffer(Buffer buffer, bool release)
+FlushLocalBuffer(Buffer buffer, bool sync, bool release)
{
int bufid;
Relation bufrel;
bufHdr = &LocalBufferDescriptors[bufid];
bufHdr->flags &= ~BM_DIRTY;
bufrel = RelationNodeCacheGetRelation(bufHdr->tag.rnode);
-
Assert(bufrel != NULL);
- smgrflush(DEFAULT_SMGR, bufrel, bufHdr->tag.blockNum,
- (char *) MAKE_PTR(bufHdr->data));
+
+ if (sync)
+ smgrflush(DEFAULT_SMGR, bufrel, bufHdr->tag.blockNum,
+ (char *) MAKE_PTR(bufHdr->data));
+ else
+ smgrwrite(DEFAULT_SMGR, bufrel, bufHdr->tag.blockNum,
+ (char *) MAKE_PTR(bufHdr->data));
+
LocalBufferFlushCount++;
- /* drop relcache refcount incremented by RelationIdCacheGetRelation */
+ /* drop relcache refcount incremented by RelationNodeCacheGetRelation */
RelationDecrementReferenceCount(bufrel);
if (release)
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/buffer/Attic/xlog_bufmgr.c,v 1.1 2000/10/28 16:20:56 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/buffer/Attic/xlog_bufmgr.c,v 1.2 2000/11/08 22:09:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
SpinRelease(BufMgrLock);
- /* drop refcnt obtained by RelationIdCacheGetRelation */
+ /* drop refcnt obtained by RelationNodeCacheGetRelation */
if (reln != (Relation) NULL)
{
RelationDecrementReferenceCount(reln);
false); /* no fsync */
}
- /* drop relcache refcnt incremented by RelationIdCacheGetRelation */
+ /* drop relcache refcnt incremented by RelationNodeCacheGetRelation */
if (reln != (Relation) NULL)
RelationDecrementReferenceCount(reln);
}
/* ---------------------------------------------------------------------
- * ReleaseRelationBuffers
+ * DropRelationBuffers
*
* This function removes all the buffered pages for a relation
* from the buffer pool. Dirty pages are simply dropped, without
- * bothering to write them out first. This is used when the
- * relation is about to be deleted. We assume that the caller
- * holds an exclusive lock on the relation, which should assure
- * that no new buffers will be acquired for the rel meanwhile.
+ * bothering to write them out first. This is NOT rollback-able,
+ * and so should be used only with extreme caution!
+ *
+ * We assume that the caller holds an exclusive lock on the relation,
+ * which should assure that no new buffers will be acquired for the rel
+ * meanwhile.
*
* XXX currently it sequentially searches the buffer pool, should be
* changed to more clever ways of searching.
* --------------------------------------------------------------------
*/
void
-ReleaseRelationBuffers(Relation rel)
+DropRelationBuffers(Relation rel)
{
int i;
BufferDesc *bufHdr;
SpinRelease(BufMgrLock);
}
+/* ---------------------------------------------------------------------
+ * DropRelFileNodeBuffers
+ *
+ * This is the same as DropRelationBuffers, except that the target
+ * relation is specified by RelFileNode.
+ *
+ * This is NOT rollback-able. One legitimate use is to clear the
+ * buffer cache of buffers for a relation that is being deleted
+ * during transaction abort.
+ * --------------------------------------------------------------------
+ */
+void
+DropRelFileNodeBuffers(RelFileNode rnode)
+{
+ int i;
+ BufferDesc *bufHdr;
+
+ /* We have to search both local and shared buffers... */
+
+ for (i = 0; i < NLocBuffer; i++)
+ {
+ bufHdr = &LocalBufferDescriptors[i];
+ if (RelFileNodeEquals(bufHdr->tag.rnode, rnode))
+ {
+ bufHdr->flags &= ~(BM_DIRTY | BM_JUST_DIRTIED);
+ bufHdr->cntxDirty = false;
+ LocalRefCount[i] = 0;
+ bufHdr->tag.rnode.relNode = InvalidOid;
+ }
+ }
+
+ SpinAcquire(BufMgrLock);
+ for (i = 1; i <= NBuffers; i++)
+ {
+ bufHdr = &BufferDescriptors[i - 1];
+recheck:
+ if (RelFileNodeEquals(bufHdr->tag.rnode, rnode))
+ {
+
+ /*
+ * If there is I/O in progress, better wait till it's done;
+ * don't want to delete the relation out from under someone
+ * who's just trying to flush the buffer!
+ */
+ if (bufHdr->flags & BM_IO_IN_PROGRESS)
+ {
+ WaitIO(bufHdr, BufMgrLock);
+
+ /*
+ * By now, the buffer very possibly belongs to some other
+ * rel, so check again before proceeding.
+ */
+ goto recheck;
+ }
+ /* Now we can do what we came for */
+ bufHdr->flags &= ~(BM_DIRTY | BM_JUST_DIRTIED);
+ bufHdr->cntxDirty = false;
+
+ /*
+ * Release any refcount we may have.
+ *
+ * This is very probably dead code, and if it isn't then it's
+ * probably wrong. I added the Assert to find out --- tgl
+ * 11/99.
+ */
+ if (!(bufHdr->flags & BM_FREE))
+ {
+ /* Assert checks that buffer will actually get freed! */
+ Assert(PrivateRefCount[i - 1] == 1 &&
+ bufHdr->refcount == 1);
+ /* ReleaseBuffer expects we do not hold the lock at entry */
+ SpinRelease(BufMgrLock);
+ ReleaseBuffer(i);
+ SpinAcquire(BufMgrLock);
+ }
+ /*
+ * And mark the buffer as no longer occupied by this rel.
+ */
+ BufTableDelete(bufHdr);
+ }
+ }
+
+ SpinRelease(BufMgrLock);
+}
+
/* ---------------------------------------------------------------------
* DropBuffers
*
* bothering to write them out first. This is used when we destroy a
* database, to avoid trying to flush data to disk when the directory
* tree no longer exists. Implementation is pretty similar to
- * ReleaseRelationBuffers() which is for destroying just one relation.
+ * DropRelationBuffers() which is for destroying just one relation.
* --------------------------------------------------------------------
*/
void
/* ---------------------------------------------------------------------
* FlushRelationBuffers
*
- * This function flushes all dirty pages of a relation out to disk.
+ * This function writes all dirty pages of a relation out to disk.
* Furthermore, pages that have blocknumber >= firstDelBlock are
* actually removed from the buffer pool. An error code is returned
* if we fail to dump a dirty buffer or if we find one of
* the target pages is pinned into the cache.
*
- * This is used by VACUUM before truncating the relation to the given
- * number of blocks. (TRUNCATE TABLE also uses it in the same way.)
- * It might seem unnecessary to flush dirty pages before firstDelBlock,
- * since VACUUM should already have committed its changes. However,
- * it is possible for there still to be dirty pages: if some page
- * had unwritten on-row tuple status updates from a prior transaction,
- * and VACUUM had no additional changes to make to that page, then
- * VACUUM won't have written it. This is harmless in most cases but
- * will break pg_upgrade, which relies on VACUUM to ensure that *all*
- * tuples have correct on-row status. So, we check and flush all
- * dirty pages of the rel regardless of block number.
+ * This is called by DROP TABLE to clear buffers for the relation
+ * from the buffer pool. Note that we must write dirty buffers,
+ * rather than just dropping the changes, because our transaction
+ * might abort later on; we want to roll back safely in that case.
*
- * This is also used by RENAME TABLE (with firstDelBlock = 0)
- * to clear out the buffer cache before renaming the physical files of
- * a relation. Without that, some other backend might try to do a
- * blind write of a buffer page (relying on the BlindId of the buffer)
- * and fail because it's not got the right filename anymore.
+ * This is also called by VACUUM before truncating the relation to the
+ * given number of blocks. It might seem unnecessary for VACUUM to
+ * write dirty pages before firstDelBlock, since VACUUM should already
+ * have committed its changes. However, it is possible for there still
+ * to be dirty pages: if some page had unwritten on-row tuple status
+ * updates from a prior transaction, and VACUUM had no additional
+ * changes to make to that page, then VACUUM won't have written it.
+ * This is harmless in most cases but will break pg_upgrade, which
+ * relies on VACUUM to ensure that *all* tuples have correct on-row
+ * status. So, we check and flush all dirty pages of the rel
+ * regardless of block number.
*
* In all cases, the caller should be holding AccessExclusiveLock on
* the target relation to ensure that no other backend is busy reading
- * more blocks of the relation.
+ * more blocks of the relation (or might do so before we commit).
*
* Formerly, we considered it an error condition if we found dirty
* buffers here. However, since BufferSync no longer forces out all
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.71 2000/07/17 03:05:08 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.72 2000/11/08 22:10:00 tgl Exp $
*
* NOTES
* Outside modules can create a lock table and acquire/release
bool
LockAcquire(LOCKMETHOD lockmethod, LOCKTAG *locktag, LOCKMODE lockmode)
{
- XIDLookupEnt *result,
+ XIDLookupEnt *xident,
item;
HTAB *xidTable;
bool found;
/*
* Find or create an xid entry with this tag
*/
- result = (XIDLookupEnt *) hash_search(xidTable, (Pointer) &item,
+ xident = (XIDLookupEnt *) hash_search(xidTable, (Pointer) &item,
HASH_ENTER, &found);
- if (!result)
+ if (!xident)
{
SpinRelease(masterLock);
elog(NOTICE, "LockAcquire: xid table corrupted");
*/
if (!found)
{
- result->nHolding = 0;
- MemSet((char *) result->holders, 0, sizeof(int) * MAX_LOCKMODES);
- ProcAddLock(&result->queue);
- XID_PRINT("LockAcquire: new", result);
+ xident->nHolding = 0;
+ MemSet((char *) xident->holders, 0, sizeof(int) * MAX_LOCKMODES);
+ ProcAddLock(&xident->queue);
+ XID_PRINT("LockAcquire: new", xident);
}
else
{
- XID_PRINT("LockAcquire: found", result);
- Assert((result->nHolding > 0) && (result->holders[lockmode] >= 0));
- Assert(result->nHolding <= lock->nActive);
+ int i;
+
+ XID_PRINT("LockAcquire: found", xident);
+ Assert((xident->nHolding > 0) && (xident->holders[lockmode] >= 0));
+ Assert(xident->nHolding <= lock->nActive);
+ /*
+ * Issue warning if we already hold a lower-level lock on this
+ * object and do not hold a lock of the requested level or higher.
+ * This indicates a deadlock-prone coding practice (eg, we'd have
+ * a deadlock if another backend were following the same code path
+ * at about the same time).
+ *
+ * XXX Doing numeric comparison on the lockmodes is a hack;
+ * it'd be better to use a table. For now, though, this works.
+ */
+ for (i = lockMethodTable->ctl->numLockModes; i > 0; i--)
+ {
+ if (xident->holders[i] > 0)
+ {
+ if (i >= (int) lockmode)
+ break; /* safe: we have a lock >= req level */
+ elog(DEBUG, "Deadlock risk: raising lock level"
+ " from %s to %s on object %u/%u/%u",
+ lock_types[i], lock_types[lockmode],
+ lock->tag.relId, lock->tag.dbId, lock->tag.objId.blkno);
+ break;
+ }
+ }
}
/* ----------------
* hold this lock.
* --------------------
*/
- if (result->nHolding == lock->nActive || result->holders[lockmode] != 0)
+ if (xident->nHolding == lock->nActive || xident->holders[lockmode] != 0)
{
- result->holders[lockmode]++;
- result->nHolding++;
- XID_PRINT("LockAcquire: owning", result);
- Assert((result->nHolding > 0) && (result->holders[lockmode] > 0));
+ xident->holders[lockmode]++;
+ xident->nHolding++;
+ XID_PRINT("LockAcquire: owning", xident);
+ Assert((xident->nHolding > 0) && (xident->holders[lockmode] > 0));
GrantLock(lock, lockmode);
SpinRelease(masterLock);
return TRUE;
* If I don't hold locks or my locks don't conflict with waiters
* then force to sleep.
*/
- if (result->nHolding > 0)
+ if (xident->nHolding > 0)
{
for (; i <= lockMethodTable->ctl->numLockModes; i++)
{
- if (result->holders[i] > 0 &&
+ if (xident->holders[i] > 0 &&
lockMethodTable->ctl->conflictTab[i] & lock->waitMask)
break; /* conflict */
}
}
- if (result->nHolding == 0 || i > lockMethodTable->ctl->numLockModes)
+ if (xident->nHolding == 0 || i > lockMethodTable->ctl->numLockModes)
{
XID_PRINT("LockAcquire: higher priority proc waiting",
- result);
+ xident);
status = STATUS_FOUND;
}
else
- status = LockResolveConflicts(lockmethod, lock, lockmode, xid, result);
+ status = LockResolveConflicts(lockmethod, lock, lockmode, xid, xident);
}
else
- status = LockResolveConflicts(lockmethod, lock, lockmode, xid, result);
+ status = LockResolveConflicts(lockmethod, lock, lockmode, xid, xident);
if (status == STATUS_OK)
GrantLock(lock, lockmode);
*/
if (lockmethod == USER_LOCKMETHOD)
{
- if (!result->nHolding)
+ if (!xident->nHolding)
{
- SHMQueueDelete(&result->queue);
- result = (XIDLookupEnt *) hash_search(xidTable,
- (Pointer) result,
+ SHMQueueDelete(&xident->queue);
+ xident = (XIDLookupEnt *) hash_search(xidTable,
+ (Pointer) xident,
HASH_REMOVE, &found);
- if (!result || !found)
+ if (!xident || !found)
elog(NOTICE, "LockAcquire: remove xid, table corrupted");
}
else
- XID_PRINT("LockAcquire: NHOLDING", result);
+ XID_PRINT("LockAcquire: NHOLDING", xident);
lock->nHolding--;
lock->holders[lockmode]--;
LOCK_PRINT("LockAcquire: user lock failed", lock, lockmode);
* Construct bitmask of locks we hold before going to sleep.
*/
MyProc->holdLock = 0;
- if (result->nHolding > 0)
+ if (xident->nHolding > 0)
{
int i,
tmpMask = 2;
for (i = 1; i <= lockMethodTable->ctl->numLockModes;
i++, tmpMask <<= 1)
{
- if (result->holders[i] > 0)
+ if (xident->holders[i] > 0)
MyProc->holdLock |= tmpMask;
}
Assert(MyProc->holdLock != 0);
* Check the xid entry status, in case something in the ipc
* communication doesn't work correctly.
*/
- if (!((result->nHolding > 0) && (result->holders[lockmode] > 0)))
+ if (!((xident->nHolding > 0) && (xident->holders[lockmode] > 0)))
{
- XID_PRINT("LockAcquire: INCONSISTENT", result);
+ XID_PRINT("LockAcquire: INCONSISTENT", xident);
LOCK_PRINT("LockAcquire: INCONSISTENT", lock, lockmode);
/* Should we retry ? */
SpinRelease(masterLock);
return FALSE;
}
- XID_PRINT("LockAcquire: granted", result);
+ XID_PRINT("LockAcquire: granted", xident);
LOCK_PRINT("LockAcquire: granted", lock, lockmode);
}
TransactionId xid,
XIDLookupEnt *xidentP) /* xident ptr or NULL */
{
- XIDLookupEnt *result,
+ XIDLookupEnt *xident,
item;
int *myHolders;
int numLockModes;
* A pointer to the xid entry was supplied from the caller.
* Actually only LockAcquire can do it.
*/
- result = xidentP;
+ xident = xidentP;
}
else
{
/*
* Find or create an xid entry with this tag
*/
- result = (XIDLookupEnt *) hash_search(xidTable, (Pointer) &item,
+ xident = (XIDLookupEnt *) hash_search(xidTable, (Pointer) &item,
HASH_ENTER, &found);
- if (!result)
+ if (!xident)
{
elog(NOTICE, "LockResolveConflicts: xid table corrupted");
return STATUS_ERROR;
* the lock stats.
* ---------------
*/
- MemSet(result->holders, 0, numLockModes * sizeof(*(lock->holders)));
- result->nHolding = 0;
- XID_PRINT("LockResolveConflicts: NOT FOUND", result);
+ MemSet(xident->holders, 0, numLockModes * sizeof(*(lock->holders)));
+ xident->nHolding = 0;
+ XID_PRINT("LockResolveConflicts: NOT FOUND", xident);
}
else
- XID_PRINT("LockResolveConflicts: found", result);
+ XID_PRINT("LockResolveConflicts: found", xident);
}
- Assert((result->nHolding >= 0) && (result->holders[lockmode] >= 0));
+ Assert((xident->nHolding >= 0) && (xident->holders[lockmode] >= 0));
/* ----------------------------
* first check for global conflicts: If no locks conflict
*/
if (!(LockMethodTable[lockmethod]->ctl->conflictTab[lockmode] & lock->mask))
{
- result->holders[lockmode]++;
- result->nHolding++;
- XID_PRINT("LockResolveConflicts: no conflict", result);
- Assert((result->nHolding > 0) && (result->holders[lockmode] > 0));
+ xident->holders[lockmode]++;
+ xident->nHolding++;
+ XID_PRINT("LockResolveConflicts: no conflict", xident);
+ Assert((xident->nHolding > 0) && (xident->holders[lockmode] > 0));
return STATUS_OK;
}
* that does not reflect our own locks.
* ------------------------
*/
- myHolders = result->holders;
+ myHolders = xident->holders;
bitmask = 0;
tmpMask = 2;
for (i = 1; i <= numLockModes; i++, tmpMask <<= 1)
if (!(LockMethodTable[lockmethod]->ctl->conflictTab[lockmode] & bitmask))
{
/* no conflict. Get the lock and go on */
- result->holders[lockmode]++;
- result->nHolding++;
- XID_PRINT("LockResolveConflicts: resolved", result);
- Assert((result->nHolding > 0) && (result->holders[lockmode] > 0));
+ xident->holders[lockmode]++;
+ xident->nHolding++;
+ XID_PRINT("LockResolveConflicts: resolved", xident);
+ Assert((xident->nHolding > 0) && (xident->holders[lockmode] > 0));
return STATUS_OK;
}
- XID_PRINT("LockResolveConflicts: conflicting", result);
+ XID_PRINT("LockResolveConflicts: conflicting", xident);
return STATUS_FOUND;
}
SPINLOCK masterLock;
bool found;
LOCKMETHODTABLE *lockMethodTable;
- XIDLookupEnt *result,
+ XIDLookupEnt *xident,
item;
HTAB *xidTable;
TransactionId xid;
* Find an xid entry with this tag
*/
xidTable = lockMethodTable->xidHash;
- result = (XIDLookupEnt *) hash_search(xidTable, (Pointer) &item,
+ xident = (XIDLookupEnt *) hash_search(xidTable, (Pointer) &item,
HASH_FIND_SAVE, &found);
- if (!result || !found)
+ if (!xident || !found)
{
SpinRelease(masterLock);
#ifdef USER_LOCKS
elog(NOTICE, "LockRelease: xid table corrupted");
return FALSE;
}
- XID_PRINT("LockRelease: found", result);
- Assert(result->tag.lock == MAKE_OFFSET(lock));
+ XID_PRINT("LockRelease: found", xident);
+ Assert(xident->tag.lock == MAKE_OFFSET(lock));
/*
* Check that we are actually holding a lock of the type we want to
* release.
*/
- if (!(result->holders[lockmode] > 0))
+ if (!(xident->holders[lockmode] > 0))
{
SpinRelease(masterLock);
- XID_PRINT("LockAcquire: WRONGTYPE", result);
+ XID_PRINT("LockAcquire: WRONGTYPE", xident);
elog(NOTICE, "LockRelease: you don't own a lock of type %s",
lock_types[lockmode]);
- Assert(result->holders[lockmode] >= 0);
+ Assert(xident->holders[lockmode] >= 0);
return FALSE;
}
- Assert(result->nHolding > 0);
+ Assert(xident->nHolding > 0);
/*
* fix the general lock stats
* now check to see if I have any private locks. If I do, decrement
* the counts associated with them.
*/
- result->holders[lockmode]--;
- result->nHolding--;
- XID_PRINT("LockRelease: updated", result);
- Assert((result->nHolding >= 0) && (result->holders[lockmode] >= 0));
+ xident->holders[lockmode]--;
+ xident->nHolding--;
+ XID_PRINT("LockRelease: updated", xident);
+ Assert((xident->nHolding >= 0) && (xident->holders[lockmode] >= 0));
/*
* If this was my last hold on this lock, delete my entry in the XID
* table.
*/
- if (!result->nHolding)
+ if (!xident->nHolding)
{
- if (result->queue.prev == INVALID_OFFSET)
+ if (xident->queue.prev == INVALID_OFFSET)
elog(NOTICE, "LockRelease: xid.prev == INVALID_OFFSET");
- if (result->queue.next == INVALID_OFFSET)
+ if (xident->queue.next == INVALID_OFFSET)
elog(NOTICE, "LockRelease: xid.next == INVALID_OFFSET");
- if (result->queue.next != INVALID_OFFSET)
- SHMQueueDelete(&result->queue);
- XID_PRINT("LockRelease: deleting", result);
- result = (XIDLookupEnt *) hash_search(xidTable, (Pointer) &result,
+ if (xident->queue.next != INVALID_OFFSET)
+ SHMQueueDelete(&xident->queue);
+ XID_PRINT("LockRelease: deleting", xident);
+ xident = (XIDLookupEnt *) hash_search(xidTable, (Pointer) &xident,
HASH_REMOVE_SAVED, &found);
- if (!result || !found)
+ if (!xident || !found)
{
SpinRelease(masterLock);
elog(NOTICE, "LockRelease: remove xid, table corrupted");
int done;
XIDLookupEnt *xidLook = NULL;
XIDLookupEnt *tmp = NULL;
- XIDLookupEnt *result;
+ XIDLookupEnt *xident;
SHMEM_OFFSET end = MAKE_OFFSET(lockQueue);
SPINLOCK masterLock;
LOCKMETHODTABLE *lockMethodTable;
*/
XID_PRINT("LockReleaseAll: deleting", xidLook);
- result = (XIDLookupEnt *) hash_search(lockMethodTable->xidHash,
+ xident = (XIDLookupEnt *) hash_search(lockMethodTable->xidHash,
(Pointer) xidLook,
HASH_REMOVE,
&found);
- if (!result || !found)
+ if (!xident || !found)
{
SpinRelease(masterLock);
elog(NOTICE, "LockReleaseAll: xid table corrupted");
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.77 2000/10/28 16:20:57 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.78 2000/11/08 22:10:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
+#include "postgres.h"
+
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/file.h>
-#include "postgres.h"
-
#include "catalog/catalog.h"
#include "miscadmin.h"
#include "storage/smgr.h"
int
mdcreate(Relation reln)
{
+ char *path;
int fd,
vfd;
- char *path;
- Assert(reln->rd_unlinked && reln->rd_fd < 0);
+ Assert(reln->rd_fd < 0);
path = relpath(reln->rd_node);
- fd = FileNameOpenFile(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY, 0600);
- /*
- * For cataloged relations, pg_class is guaranteed to have a unique
- * record with the same relname by the unique index. So we are able to
- * reuse existent files for new cataloged relations. Currently we reuse
- * them in the following cases. 1. they are empty. 2. they are used
- * for Index relations and their size == BLCKSZ * 2.
- *
- * During bootstrap processing, we skip that check, because pg_time,
- * pg_variable, and pg_log get created before their .bki file entries
- * are processed.
- */
+ fd = FileNameOpenFile(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY, 0600);
if (fd < 0)
{
int save_errno = errno;
- if (!IsBootstrapProcessingMode() &&
- reln->rd_rel->relkind == RELKIND_UNCATALOGED)
- return -1;
-
- fd = FileNameOpenFile(path, O_RDWR | PG_BINARY, 0600);
+ /*
+ * During bootstrap, there are cases where a system relation will be
+ * accessed (by internal backend processes) before the bootstrap
+ * script nominally creates it. Therefore, allow the file to exist
+ * already, but in bootstrap mode only. (See also mdopen)
+ */
+ if (IsBootstrapProcessingMode())
+ fd = FileNameOpenFile(path, O_RDWR | PG_BINARY, 0600);
if (fd < 0)
{
+ pfree(path);
/* be sure to return the error reported by create, not open */
errno = save_errno;
return -1;
}
- if (!IsBootstrapProcessingMode())
- {
- bool reuse = false;
- long len = FileSeek(fd, 0L, SEEK_END);
-
- if (len == 0)
- reuse = true;
- else if (reln->rd_rel->relkind == RELKIND_INDEX &&
- len == BLCKSZ * 2)
- reuse = true;
- if (!reuse)
- {
- FileClose(fd);
- /* be sure to return the error reported by create */
- errno = save_errno;
- return -1;
- }
- }
errno = 0;
}
- reln->rd_unlinked = false;
+
+ pfree(path);
vfd = _fdvec_alloc();
if (vfd < 0)
Md_fdvec[vfd].mdfd_vfd = fd;
Md_fdvec[vfd].mdfd_flags = (uint16) 0;
+ Md_fdvec[vfd].mdfd_lstbcnt = 0;
#ifndef LET_OS_MANAGE_FILESIZE
Md_fdvec[vfd].mdfd_chain = (MdfdVec *) NULL;
#endif
- Md_fdvec[vfd].mdfd_lstbcnt = 0;
-
- pfree(path);
return vfd;
}
* mdunlink() -- Unlink a relation.
*/
int
-mdunlink(Relation reln)
+mdunlink(RelFileNode rnode)
{
- int nblocks;
- int fd;
- MdfdVec *v;
-
- /*
- * If the relation is already unlinked,we have nothing to do any more.
- */
- if (reln->rd_unlinked && reln->rd_fd < 0)
- return SM_SUCCESS;
-
- /*
- * Force all segments of the relation to be opened, so that we won't
- * miss deleting any of them.
- */
- nblocks = mdnblocks(reln);
+ int status = SM_SUCCESS;
+ int save_errno = 0;
+ char *path;
- /*
- * Clean out the mdfd vector, letting fd.c unlink the physical files.
- *
- * NOTE: We truncate the file(s) before deleting 'em, because if other
- * backends are holding the files open, the unlink will fail on some
- * platforms (think Microsoft). Better a zero-size file gets left
- * around than a big file. Those other backends will be forced to
- * close the relation by cache invalidation, but that probably hasn't
- * happened yet.
- */
- fd = RelationGetFile(reln);
- if (fd < 0) /* should not happen */
- elog(ERROR, "mdunlink: mdnblocks didn't open relation");
+ path = relpath(rnode);
- Md_fdvec[fd].mdfd_flags = (uint16) 0;
+ /* Delete the first segment, or only segment if not doing segmenting */
+ if (unlink(path) < 0)
+ {
+ status = SM_FAIL;
+ save_errno = errno;
+ }
#ifndef LET_OS_MANAGE_FILESIZE
- for (v = &Md_fdvec[fd]; v != (MdfdVec *) NULL;)
+ /* Get the additional segments, if any */
+ if (status == SM_SUCCESS)
{
- MdfdVec *ov = v;
+ char *segpath = (char *) palloc(strlen(path) + 12);
+ int segno;
- FileTruncate(v->mdfd_vfd, 0);
- FileUnlink(v->mdfd_vfd);
- v = v->mdfd_chain;
- if (ov != &Md_fdvec[fd])
- pfree(ov);
+ for (segno = 1; ; segno++)
+ {
+ sprintf(segpath, "%s.%d", path, segno);
+ if (unlink(segpath) < 0)
+ {
+ /* ENOENT is expected after the last segment... */
+ if (errno != ENOENT)
+ {
+ status = SM_FAIL;
+ save_errno = errno;
+ }
+ break;
+ }
+ }
+ pfree(segpath);
}
- Md_fdvec[fd].mdfd_chain = (MdfdVec *) NULL;
-#else
- v = &Md_fdvec[fd];
- FileTruncate(v->mdfd_vfd, 0);
- FileUnlink(v->mdfd_vfd);
#endif
- _fdvec_free(fd);
-
- /* be sure to mark relation closed && unlinked */
- reln->rd_fd = -1;
- reln->rd_unlinked = true;
+ pfree(path);
- return SM_SUCCESS;
+ errno = save_errno;
+ return status;
}
/*
int vfd;
Assert(reln->rd_fd < 0);
+
path = relpath(reln->rd_node);
fd = FileNameOpenFile(path, O_RDWR | PG_BINARY, 0600);
+
if (fd < 0)
{
- /* in bootstrap mode, accept mdopen as substitute for mdcreate */
+ /*
+ * During bootstrap, there are cases where a system relation will be
+ * accessed (by internal backend processes) before the bootstrap
+ * script nominally creates it. Therefore, accept mdopen() as a
+ * substitute for mdcreate() in bootstrap mode only. (See mdcreate)
+ */
if (IsBootstrapProcessingMode())
fd = FileNameOpenFile(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY, 0600);
if (fd < 0)
{
- elog(NOTICE, "mdopen: couldn't open %s: %m", path);
- /* mark relation closed and unlinked */
- reln->rd_fd = -1;
- reln->rd_unlinked = true;
+ pfree(path);
return -1;
}
}
- reln->rd_unlinked = false;
+
+ pfree(path);
vfd = _fdvec_alloc();
if (vfd < 0)
#endif
#endif
- pfree(path);
-
return vfd;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/smgr/Attic/mm.c,v 1.19 2000/04/10 23:41:51 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/smgr/Attic/mm.c,v 1.20 2000/11/08 22:10:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* mmunlink() -- Unlink a relation.
+ *
+ * XXX currently broken: needs to accept RelFileNode, not Relation
*/
int
-mmunlink(Relation reln)
+mmunlink(RelFileNode rnode)
{
int i;
Oid reldbid;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/smgr/smgr.c,v 1.42 2000/10/28 16:20:57 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/smgr/smgr.c,v 1.43 2000/11/08 22:10:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "storage/bufmgr.h"
#include "storage/smgr.h"
+#include "utils/memutils.h"
+
static void smgrshutdown(void);
int (*smgr_init) (void); /* may be NULL */
int (*smgr_shutdown) (void); /* may be NULL */
int (*smgr_create) (Relation reln);
- int (*smgr_unlink) (Relation reln);
+ int (*smgr_unlink) (RelFileNode rnode);
int (*smgr_extend) (Relation reln, char *buffer);
int (*smgr_open) (Relation reln);
int (*smgr_close) (Relation reln);
{mdinit, NULL, mdcreate, mdunlink, mdextend, mdopen, mdclose,
mdread, mdwrite, mdflush, mdblindwrt, mdmarkdirty, mdblindmarkdirty,
#ifdef XLOG
- mdnblocks, mdtruncate, mdcommit, mdabort, mdsync},
+ mdnblocks, mdtruncate, mdcommit, mdabort, mdsync
#else
- mdnblocks, mdtruncate, mdcommit, mdabort},
+ mdnblocks, mdtruncate, mdcommit, mdabort
#endif
+ },
#ifdef STABLE_MEMORY_STORAGE
/* main memory */
static int NSmgr = lengthof(smgrsw);
+/*
+ * We keep a list of all relations (represented as RelFileNode values)
+ * that have been created or deleted in the current transaction. When
+ * a relation is created, we create the physical file immediately, but
+ * remember it so that we can delete the file again if the current
+ * transaction is aborted. Conversely, a deletion request is NOT
+ * executed immediately, but is just entered in the list. When and if
+ * the transaction commits, we can delete the physical file.
+ *
+ * NOTE: the list is kept in TopMemoryContext to be sure it won't disappear
+ * unbetimes. It'd probably be OK to keep it in TopTransactionContext,
+ * but I'm being paranoid.
+ */
+
+typedef struct PendingRelDelete
+{
+ RelFileNode relnode; /* relation that may need to be deleted */
+ int16 which; /* which storage manager? */
+ bool atCommit; /* T=delete at commit; F=delete at abort */
+ struct PendingRelDelete *next; /* linked-list link */
+} PendingRelDelete;
+
+static PendingRelDelete *pendingDeletes = NULL; /* head of linked list */
+
+
/*
* smgrinit(), smgrshutdown() -- Initialize or shut down all storage
* managers.
smgrcreate(int16 which, Relation reln)
{
int fd;
+ PendingRelDelete *pending;
if ((fd = (*(smgrsw[which].smgr_create)) (reln)) < 0)
elog(ERROR, "cannot create %s: %m", RelationGetRelationName(reln));
+ /* Add the relation to the list of stuff to delete at abort */
+ pending = (PendingRelDelete *)
+ MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
+ pending->relnode = reln->rd_node;
+ pending->which = which;
+ pending->atCommit = false; /* delete if abort */
+ pending->next = pendingDeletes;
+ pendingDeletes = pending;
+
return fd;
}
/*
* smgrunlink() -- Unlink a relation.
*
- * The relation is removed from the store.
+ * The relation is removed from the store. Actually, we just remember
+ * that we want to do this at transaction commit.
*/
int
smgrunlink(int16 which, Relation reln)
{
- int status;
-
- if ((status = (*(smgrsw[which].smgr_unlink)) (reln)) == SM_FAIL)
- elog(ERROR, "cannot unlink %s: %m", RelationGetRelationName(reln));
+ PendingRelDelete *pending;
+
+ /* Make sure the file is closed */
+ if (reln->rd_fd >= 0)
+ smgrclose(which, reln);
+
+ /* Add the relation to the list of stuff to delete at commit */
+ pending = (PendingRelDelete *)
+ MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
+ pending->relnode = reln->rd_node;
+ pending->which = which;
+ pending->atCommit = true; /* delete if commit */
+ pending->next = pendingDeletes;
+ pendingDeletes = pending;
+
+ /*
+ * NOTE: if the relation was created in this transaction, it will now
+ * be present in the pending-delete list twice, once with atCommit true
+ * and once with atCommit false. Hence, it will be physically deleted
+ * at end of xact in either case (and the other entry will be ignored
+ * by smgrDoPendingDeletes, so no error will occur). We could instead
+ * remove the existing list entry and delete the physical file
+ * immediately, but for now I'll keep the logic simple.
+ */
- return status;
+ return SM_SUCCESS;
}
/*
/*
* smgropen() -- Open a relation using a particular storage manager.
*
- * Returns the fd for the open relation on success, aborts the
- * transaction on failure.
+ * Returns the fd for the open relation on success.
+ *
+ * On failure, returns -1 if failOK, else aborts the transaction.
*/
int
-smgropen(int16 which, Relation reln)
+smgropen(int16 which, Relation reln, bool failOK)
{
int fd;
- if ((fd = (*(smgrsw[which].smgr_open)) (reln)) < 0 &&
- !reln->rd_unlinked)
- elog(ERROR, "cannot open %s: %m", RelationGetRelationName(reln));
+ if ((fd = (*(smgrsw[which].smgr_open)) (reln)) < 0)
+ if (! failOK)
+ elog(ERROR, "cannot open %s: %m", RelationGetRelationName(reln));
return fd;
}
/*
* smgrclose() -- Close a relation.
*
- * NOTE: underlying manager should allow case where relation is
- * already closed. Indeed relation may have been unlinked!
- * This is currently called only from RelationFlushRelation() when
- * the relation cache entry is about to be dropped; could be doing
- * simple relation cache clear, or finishing up DROP TABLE.
- *
* Returns SM_SUCCESS on success, aborts on failure.
*/
int
return newblks;
}
+/*
+ * smgrDoPendingDeletes() -- take care of relation deletes at end of xact.
+ */
+int
+smgrDoPendingDeletes(bool isCommit)
+{
+ while (pendingDeletes != NULL)
+ {
+ PendingRelDelete *pending = pendingDeletes;
+
+ pendingDeletes = pending->next;
+ if (pending->atCommit == isCommit)
+ {
+ /*
+ * Get rid of any leftover buffers for the rel (shouldn't be
+ * any in the commit case, but there can be in the abort case).
+ */
+ DropRelFileNodeBuffers(pending->relnode);
+ /*
+ * And delete the physical files.
+ *
+ * Note: we treat deletion failure as a NOTICE, not an error,
+ * because we've already decided to commit or abort the current
+ * xact.
+ */
+ if ((*(smgrsw[pending->which].smgr_unlink)) (pending->relnode) == SM_FAIL)
+ elog(NOTICE, "cannot unlink %u/%u: %m",
+ pending->relnode.tblNode, pending->relnode.relNode);
+ }
+ pfree(pending);
+ }
+
+ return SM_SUCCESS;
+}
+
/*
* smgrcommit(), smgrabort() -- Commit or abort changes made during the
* current transaction.
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.37 2000/06/08 19:51:03 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.38 2000/11/08 22:10:01 tgl Exp $
*
* Note - this code is real crufty...
*
/*
* ----------------
- * Invalidation info was devided into three parts.
- * 1) shared invalidation to be registerd for all backends
+ * Invalidation info is divided into three parts.
+ * 1) shared invalidation to be registered for all backends
* 2) local invalidation for the transaction itself
- * 3) rollback information for the transaction itself
+ * 3) rollback information for the transaction itself (in case we abort)
* ----------------
*/
* --------------------------------
*/
static void
- LocalInvalidInvalidate(LocalInvalid invalid, void (*function) (), bool freemember)
+LocalInvalidInvalidate(LocalInvalid invalid,
+ void (*function) (),
+ bool freemember)
{
InvalidationEntryData *entryDataP;
elog(DEBUG, "CacheIdRegisterLocalRollback(%d, %d, [%d, %d])", \
cacheId, hashIndex, ItemPointerGetBlockNumber(pointer), \
ItemPointerGetOffsetNumber(pointer))
-#define CacheIdImmediateRegisterSharedInvalid_DEBUG1 \
-elog(DEBUG, "CacheIdImmediateRegisterSharedInvalid(%d, %d, [%d, %d])", \
- cacheId, hashIndex, ItemPointerGetBlockNumber(pointer), \
- ItemPointerGetOffsetNumber(pointer))
#else
#define CacheIdRegisterSpecifiedLocalInvalid_DEBUG1
#define CacheIdRegisterLocalInvalid_DEBUG1
#define CacheIdRegisterLocalRollback_DEBUG1
-#define CacheIdImmediateRegisterSharedInvalid_DEBUG1
#endif /* INVALIDDEBUG */
/* --------------------------------
*/
static LocalInvalid
CacheIdRegisterSpecifiedLocalInvalid(LocalInvalid invalid,
- Index cacheId, Index hashIndex, ItemPointer pointer)
+ Index cacheId,
+ Index hashIndex,
+ ItemPointer pointer)
{
InvalidationMessage message;
RollbackStack, cacheId, hashIndex, pointer);
}
-/* --------------------------------
- * CacheIdImmediateRegisterSharedInvalid
- * --------------------------------
- */
-static void
-CacheIdImmediateRegisterSharedInvalid(Index cacheId, Index hashIndex,
- ItemPointer pointer)
-{
- InvalidationMessage message;
-
- /* ----------------
- * debugging stuff
- * ----------------
- */
- CacheIdImmediateRegisterSharedInvalid_DEBUG1;
-
- /* ----------------
- * create a message describing the system catalog tuple
- * we wish to invalidate.
- * ----------------
- */
- message = (InvalidationMessage)
- InvalidationEntryAllocate(sizeof(InvalidationMessageData));
-
- message->kind = 'c';
- message->any.catalog.cacheId = cacheId;
- message->any.catalog.hashIndex = hashIndex;
-
- ItemPointerCopy(pointer, &message->any.catalog.pointerData);
- /* ----------------
- * Register a shared catalog cache invalidation.
- * ----------------
- */
- InvalidationMessageRegisterSharedInvalid(message);
- free((Pointer) &((InvalidationUserData *) message)->dataP[-1]);
-}
-
/* --------------------------------
* RelationIdRegisterSpecifiedLocalInvalid
* --------------------------------
RollbackStack, relationId, objectId);
}
-/* --------------------------------
- * RelationIdImmediateRegisterSharedInvalid
- * --------------------------------
- */
-static void
-RelationIdImmediateRegisterSharedInvalid(Oid relationId, Oid objectId)
-{
- InvalidationMessage message;
-
- /* ----------------
- * debugging stuff
- * ----------------
- */
-#ifdef INVALIDDEBUG
- elog(DEBUG, "RelationImmediateRegisterSharedInvalid(%u, %u)", relationId,
- objectId);
-#endif /* defined(INVALIDDEBUG) */
-
- /* ----------------
- * create a message describing the relation descriptor
- * we wish to invalidate.
- * ----------------
- */
- message = (InvalidationMessage)
- InvalidationEntryAllocate(sizeof(InvalidationMessageData));
-
- message->kind = 'r';
- message->any.relation.relationId = relationId;
- message->any.relation.objectId = objectId;
-
- /* ----------------
- * Register a shared catalog cache invalidation.
- * ----------------
- */
- InvalidationMessageRegisterSharedInvalid(message);
- free((Pointer) &((InvalidationUserData *) message)->dataP[-1]);
-}
-
/* --------------------------------
* CacheIdInvalidate
*
RelationIdRegisterLocalRollback,
"RelationMark4RollbackHeapTuple");
}
-
-/*
- * ImmediateInvalidateSharedHeapTuple
- * Different from RelationInvalidateHeapTuple()
- * this function queues shared invalidation info immediately.
- */
-void
-ImmediateInvalidateSharedHeapTuple(Relation relation, HeapTuple tuple)
-{
- InvokeHeapTupleInvalidation(relation, tuple,
- CacheIdImmediateRegisterSharedInvalid,
- RelationIdImmediateRegisterSharedInvalid,
- "ImmediateInvalidateSharedHeapTuple");
-}
-
-#ifdef NOT_USED
-/*
- * ImmediateSharedRelationCacheInvalidate
- * Register shared relation cache invalidation immediately
- *
- * This is needed for smgrunlink()/smgrtruncate().
- * Those functions unlink/truncate the base file immediately
- * and couldn't be rollbacked in case of abort/crash.
- * So relation cache invalidation must be registerd immediately.
- * Note:
- * Assumes Relation is valid.
- */
-void
-ImmediateSharedRelationCacheInvalidate(Relation relation)
-{
- /* ----------------
- * sanity checks
- * ----------------
- */
- Assert(RelationIsValid(relation));
-
- if (IsBootstrapProcessingMode())
- return;
-
- /* ----------------
- * debugging stuff
- * ----------------
- */
-#ifdef INVALIDDEBUG
- elog(DEBUG, "ImmediateSharedRelationCacheInvalidate(%s)", \
- RelationGetPhysicalRelationName(relation));
-#endif /* defined(INVALIDDEBUG) */
-
- RelationIdImmediateRegisterSharedInvalid(
- RelOid_pg_class, RelationGetRelid(relation));
-}
-#endif
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.114 2000/10/28 16:20:57 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.115 2000/11/08 22:10:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
RelationBuildDesc(RelationBuildDescInfo buildinfo,
Relation oldrelation)
{
- File fd;
Relation relation;
Oid relid;
Oid relam;
* by the storage manager code to rd_fd.
* ----------------
*/
- if (relation->rd_rel->relkind != RELKIND_VIEW) {
- fd = smgropen(DEFAULT_SMGR, relation);
-
- Assert(fd >= -1);
- if (fd == -1)
- elog(NOTICE, "RelationBuildDesc: smgropen(%s): %m",
- NameStr(relation->rd_rel->relname));
-
- relation->rd_fd = fd;
- } else {
+ if (relation->rd_rel->relkind != RELKIND_VIEW)
+ relation->rd_fd = smgropen(DEFAULT_SMGR, relation, false);
+ else
relation->rd_fd = -1;
- }
/* ----------------
* insert newly created relation into proper relcaches,
if (RelationIsValid(rd))
{
+ /* re-open files if necessary */
if (rd->rd_fd == -1 && rd->rd_rel->relkind != RELKIND_VIEW)
- {
- rd->rd_fd = smgropen(DEFAULT_SMGR, rd);
- Assert(rd->rd_fd != -1 || rd->rd_unlinked);
- }
+ rd->rd_fd = smgropen(DEFAULT_SMGR, rd, false);
RelationIncrementReferenceCount(rd);
-
}
return rd;
if (RelationIsValid(rd))
{
+ /* re-open files if necessary */
if (rd->rd_fd == -1 && rd->rd_rel->relkind != RELKIND_VIEW)
- {
- rd->rd_fd = smgropen(DEFAULT_SMGR, rd);
- Assert(rd->rd_fd != -1 || rd->rd_unlinked);
- }
+ rd->rd_fd = smgropen(DEFAULT_SMGR, rd, false);
RelationIncrementReferenceCount(rd);
-
}
return rd;
if (RelationIsValid(rd))
{
+ /* re-open files if necessary */
if (rd->rd_fd == -1 && rd->rd_rel->relkind != RELKIND_VIEW)
- {
- rd->rd_fd = smgropen(DEFAULT_SMGR, rd);
- Assert(rd->rd_fd != -1 || rd->rd_unlinked);
- }
+ rd->rd_fd = smgropen(DEFAULT_SMGR, rd, false);
RelationIncrementReferenceCount(rd);
-
}
return rd;
/*
* Make sure smgr and lower levels close the relation's files, if they
- * weren't closed already. We do this unconditionally; if the
- * relation is not deleted, the next smgr access should reopen the
- * files automatically. This ensures that the low-level file access
- * state is updated after, say, a vacuum truncation.
- *
- * NOTE: this call is a no-op if the relation's smgr file is already
- * closed or unlinked.
+ * weren't closed already. If the relation is not getting deleted,
+ * the next smgr access should reopen the files automatically. This
+ * ensures that the low-level file access state is updated after, say,
+ * a vacuum truncation.
*/
- smgrclose(DEFAULT_SMGR, relation);
+ if (relation->rd_fd >= 0)
+ smgrclose(DEFAULT_SMGR, relation);
/*
* Never, never ever blow away a nailed-in system relation, because
MemoryContext old_rulescxt = relation->rd_rulescxt;
TriggerDesc *old_trigdesc = relation->trigdesc;
int old_nblocks = relation->rd_nblocks;
- bool relDescChanged = false;
RelationBuildDescInfo buildinfo;
buildinfo.infotype = INFO_RELID;
else
{
FreeTupleDesc(old_att);
- relDescChanged = true;
}
if (equalRuleLocks(old_rules, relation->rd_rules))
{
{
if (old_rulescxt)
MemoryContextDelete(old_rulescxt);
- relDescChanged = true;
}
if (equalTriggerDescs(old_trigdesc, relation->trigdesc))
{
else
{
FreeTriggerDesc(old_trigdesc);
- relDescChanged = true;
}
relation->rd_nblocks = old_nblocks;
* this is kind of expensive, but I think we must do it in case
* relation has been truncated...
*/
- if (relation->rd_unlinked)
- relation->rd_nblocks = 0;
- else
- relation->rd_nblocks = RelationGetNumberOfBlocks(relation);
-
- if (relDescChanged && !RelationHasReferenceCountZero(relation))
- elog(ERROR, "RelationClearRelation: relation %u modified while in use",
- buildinfo.i.info_id);
+ relation->rd_nblocks = RelationGetNumberOfBlocks(relation);
}
}
void
RelationPurgeLocalRelation(bool xactCommitted)
{
- if (newlyCreatedRelns == NULL)
- return;
-
while (newlyCreatedRelns)
{
List *l = newlyCreatedRelns;
newlyCreatedRelns = lnext(newlyCreatedRelns);
pfree(l);
- if (!xactCommitted)
- {
- /*
- * remove the file if we abort. This is so that files for
- * tables created inside a transaction block get removed.
- */
- if (! reln->rd_unlinked)
- {
- smgrunlink(DEFAULT_SMGR, reln);
- reln->rd_unlinked = true;
- }
- }
-
+ /* XXX is this step still needed? If so, why? */
if (!IsBootstrapProcessingMode())
RelationClearRelation(reln, false);
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/temprel.c,v 1.29 2000/10/19 23:06:24 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/temprel.c,v 1.30 2000/11/08 22:10:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* to drop the underlying physical relations at session shutdown.
*/
-#include <sys/types.h>
-
#include "postgres.h"
+#include <sys/types.h>
+
#include "catalog/heap.h"
#include "catalog/index.h"
#include "miscadmin.h"
typedef struct TempTable
{
- char *user_relname; /* logical name of temp table */
- char *relname; /* underlying unique name */
+ NameData user_relname; /* logical name of temp table */
+ NameData relname; /* underlying unique name */
Oid relid; /* needed properties of rel */
char relkind;
- TransactionId xid; /* xact in which temp tab was created */
+ /*
+ * If this entry was created during this xact, it should be deleted
+ * at xact abort. Conversely, if this entry was deleted during this
+ * xact, it should be removed at xact commit. We leave deleted entries
+ * in the list until commit so that we can roll back if needed ---
+ * but we ignore them for purposes of lookup!
+ */
+ bool created_in_cur_xact;
+ bool deleted_in_cur_xact;
} TempTable;
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
temp_rel = (TempTable *) palloc(sizeof(TempTable));
- temp_rel->user_relname = (char *) palloc(NAMEDATALEN);
- temp_rel->relname = (char *) palloc(NAMEDATALEN);
- StrNCpy(temp_rel->user_relname, relname, NAMEDATALEN);
- StrNCpy(temp_rel->relname, NameStr(pg_class_form->relname), NAMEDATALEN);
+ StrNCpy(NameStr(temp_rel->user_relname), relname,
+ NAMEDATALEN);
+ StrNCpy(NameStr(temp_rel->relname), NameStr(pg_class_form->relname),
+ NAMEDATALEN);
temp_rel->relid = pg_class_tuple->t_data->t_oid;
temp_rel->relkind = pg_class_form->relkind;
- temp_rel->xid = GetCurrentTransactionId();
+ temp_rel->created_in_cur_xact = true;
+ temp_rel->deleted_in_cur_xact = false;
temp_rels = lcons(temp_rel, temp_rels);
MemoryContextSwitchTo(oldcxt);
}
+/*
+ * Remove a temp relation map entry (part of DROP TABLE on a temp table).
+ * We don't actually remove the entry, just mark it dead.
+ *
+ * We don't have the relname for indexes, so we just pass the oid.
+ */
+void
+remove_temp_rel_by_relid(Oid relid)
+{
+ List *l;
+
+ foreach(l, temp_rels)
+ {
+ TempTable *temp_rel = (TempTable *) lfirst(l);
+
+ if (temp_rel->relid == relid)
+ temp_rel->deleted_in_cur_xact = true;
+ /* Keep scanning 'cause there could be multiple matches; see RENAME */
+ }
+}
+
+/*
+ * To implement ALTER TABLE RENAME on a temp table, we shouldn't touch
+ * the underlying physical table at all, just change the map entry!
+ *
+ * This routine is invoked early in ALTER TABLE RENAME to check for
+ * the temp-table case. If oldname matches a temp table name, change
+ * the mapping to the new logical name and return TRUE (or elog if
+ * there is a conflict with another temp table name). If there is
+ * no match, return FALSE indicating that normal rename should proceed.
+ *
+ * We also reject an attempt to rename a normal table to a name in use
+ * as a temp table name. That would fail later on anyway when rename.c
+ * looks for a rename conflict, but we can give a more specific error
+ * message for the problem here.
+ *
+ * It might seem that we need to check for attempts to rename the physical
+ * file underlying a temp table, but that'll be rejected anyway because
+ * pg_tempXXX looks like a system table name.
+ */
+bool
+rename_temp_relation(const char *oldname,
+ const char *newname)
+{
+ List *l;
+
+ foreach(l, temp_rels)
+ {
+ TempTable *temp_rel = (TempTable *) lfirst(l);
+ MemoryContext oldcxt;
+ TempTable *new_temp_rel;
+
+ if (temp_rel->deleted_in_cur_xact)
+ continue; /* ignore it if logically deleted */
+
+ if (strcmp(NameStr(temp_rel->user_relname), oldname) != 0)
+ continue; /* ignore non-matching entries */
+
+ /* We are renaming a temp table --- is it OK to do so? */
+ if (get_temp_rel_by_username(newname) != NULL)
+ elog(ERROR, "Cannot rename temp table \"%s\": temp table \"%s\" already exists",
+ oldname, newname);
+
+ /*
+ * Create a new mapping entry and mark the old one deleted in this
+ * xact. One of these entries will be deleted at xact end.
+ */
+ oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+ new_temp_rel = (TempTable *) palloc(sizeof(TempTable));
+ memcpy(new_temp_rel, temp_rel, sizeof(TempTable));
+
+ StrNCpy(NameStr(new_temp_rel->user_relname), newname, NAMEDATALEN);
+ new_temp_rel->created_in_cur_xact = true;
+
+ temp_rels = lcons(new_temp_rel, temp_rels);
+
+ temp_rel->deleted_in_cur_xact = true;
+
+ MemoryContextSwitchTo(oldcxt);
+
+ return true;
+ }
+
+ /* Old name does not match any temp table name, what about new? */
+ if (get_temp_rel_by_username(newname) != NULL)
+ elog(ERROR, "Cannot rename \"%s\" to \"%s\": a temp table by that name already exists",
+ oldname, newname);
+
+ return false;
+}
+
+
/*
* Remove underlying relations for all temp rels at backend shutdown.
*/
void
remove_all_temp_relations(void)
{
+ List *l;
+
/* skip xact start overhead if nothing to do */
if (temp_rels == NIL)
return;
StartTransactionCommand();
/*
- * The way this works is that each time through the loop, we delete
- * the frontmost entry. The DROP will call remove_temp_rel_by_relid()
- * as a side effect, thereby removing the entry in the temp_rels list.
- * So this is not an infinite loop, even though it looks like one.
+ * Scan the list and delete all entries not already deleted.
+ * We need not worry about list entries getting deleted from under us,
+ * because remove_temp_rel_by_relid() doesn't remove entries, only
+ * mark them dead.
*/
- while (temp_rels != NIL)
+ foreach(l, temp_rels)
{
- TempTable *temp_rel = (TempTable *) lfirst(temp_rels);
+ TempTable *temp_rel = (TempTable *) lfirst(l);
+
+ if (temp_rel->deleted_in_cur_xact)
+ continue; /* ignore it if deleted already */
if (temp_rel->relkind != RELKIND_INDEX)
{
char relname[NAMEDATALEN];
/* safe from deallocation */
- strcpy(relname, temp_rel->user_relname);
+ strcpy(relname, NameStr(temp_rel->user_relname));
heap_drop_with_catalog(relname, allowSystemTableMods);
}
else
}
/*
- * Remove a temp relation map entry (part of DROP TABLE on a temp table)
+ * Clean up temprel mapping entries during transaction commit or abort.
*
- * we don't have the relname for indexes, so we just pass the oid
- */
-void
-remove_temp_rel_by_relid(Oid relid)
-{
- MemoryContext oldcxt;
- List *l,
- *prev;
-
- oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-
- prev = NIL;
- l = temp_rels;
- while (l != NIL)
- {
- TempTable *temp_rel = (TempTable *) lfirst(l);
-
- if (temp_rel->relid == relid)
- {
- pfree(temp_rel->user_relname);
- pfree(temp_rel->relname);
- pfree(temp_rel);
- /* remove from linked list */
- if (prev != NIL)
- {
- lnext(prev) = lnext(l);
- pfree(l);
- l = lnext(prev);
- }
- else
- {
- temp_rels = lnext(l);
- pfree(l);
- l = temp_rels;
- }
- }
- else
- {
- prev = l;
- l = lnext(l);
- }
- }
-
- MemoryContextSwitchTo(oldcxt);
-}
-
-/*
- * Remove freshly-created map entries during transaction abort.
+ * During commit, remove entries that were deleted during this transaction;
+ * during abort, remove those created during this transaction.
*
- * The underlying physical rel will be removed by normal abort processing.
- * We just have to delete the map entry.
+ * We do not need to worry about removing the underlying physical relation;
+ * that's someone else's job.
*/
void
-remove_temp_rel_in_myxid(void)
+AtEOXact_temp_relations(bool isCommit)
{
- MemoryContext oldcxt;
List *l,
*prev;
- oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-
prev = NIL;
l = temp_rels;
while (l != NIL)
{
TempTable *temp_rel = (TempTable *) lfirst(l);
- if (temp_rel->xid == GetCurrentTransactionId())
+ if (isCommit ? temp_rel->deleted_in_cur_xact :
+ temp_rel->created_in_cur_xact)
{
- pfree(temp_rel->user_relname);
- pfree(temp_rel->relname);
+ /* This entry must be removed */
pfree(temp_rel);
/* remove from linked list */
if (prev != NIL)
}
else
{
+ /* This entry must be preserved */
+ temp_rel->created_in_cur_xact = false;
+ temp_rel->deleted_in_cur_xact = false;
prev = l;
l = lnext(l);
}
}
-
- MemoryContextSwitchTo(oldcxt);
-}
-
-/*
- * To implement ALTER TABLE RENAME on a temp table, we shouldn't touch
- * the underlying physical table at all, just change the map entry!
- *
- * This routine is invoked early in ALTER TABLE RENAME to check for
- * the temp-table case. If oldname matches a temp table name, change
- * the map entry to the new logical name and return TRUE (or elog if
- * there is a conflict with another temp table name). If there is
- * no match, return FALSE indicating that normal rename should proceed.
- *
- * We also reject an attempt to rename a normal table to a name in use
- * as a temp table name. That would fail later on anyway when rename.c
- * looks for a rename conflict, but we can give a more specific error
- * message for the problem here.
- *
- * It might seem that we need to check for attempts to rename the physical
- * file underlying a temp table, but that'll be rejected anyway because
- * pg_tempXXX looks like a system table name.
- *
- * A nitpicker might complain that the rename should be undone if the
- * current xact is later aborted, but I'm not going to fix that now.
- * This whole mapping mechanism ought to be replaced with something
- * schema-based, anyhow.
- */
-bool
-rename_temp_relation(const char *oldname,
- const char *newname)
-{
- List *l;
-
- foreach(l, temp_rels)
- {
- TempTable *temp_rel = (TempTable *) lfirst(l);
-
- if (strcmp(temp_rel->user_relname, oldname) == 0)
- {
- if (get_temp_rel_by_username(newname) != NULL)
- elog(ERROR, "Cannot rename temp table \"%s\": temp table \"%s\" already exists",
- oldname, newname);
- /* user_relname was palloc'd NAMEDATALEN, so safe to re-use it */
- StrNCpy(temp_rel->user_relname, newname, NAMEDATALEN);
- return true;
- }
- }
-
- /* Old name does not match any temp table name, what about new? */
- if (get_temp_rel_by_username(newname) != NULL)
- elog(ERROR, "Cannot rename \"%s\" to \"%s\": a temp table by that name already exists",
- oldname, newname);
-
- return false;
}
{
TempTable *temp_rel = (TempTable *) lfirst(l);
- if (strcmp(temp_rel->user_relname, user_relname) == 0)
- return temp_rel->relname;
+ if (temp_rel->deleted_in_cur_xact)
+ continue; /* ignore it if logically deleted */
+
+ if (strcmp(NameStr(temp_rel->user_relname), user_relname) == 0)
+ return NameStr(temp_rel->relname);
}
return NULL;
}
{
TempTable *temp_rel = (TempTable *) lfirst(l);
- if (strcmp(temp_rel->relname, relname) == 0)
- return temp_rel->user_relname;
+ if (temp_rel->deleted_in_cur_xact)
+ continue; /* ignore it if logically deleted */
+
+ if (strcmp(NameStr(temp_rel->relname), relname) == 0)
+ return NameStr(temp_rel->user_relname);
}
/* needed for bootstrapping temp tables */
return pstrdup(relname);
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: heap.h,v 1.31 2000/07/04 06:11:54 tgl Exp $
+ * $Id: heap.h,v 1.32 2000/11/08 22:10:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
bool istemp, bool storage_create,
bool allow_system_table_mods);
-extern bool heap_storage_create(Relation rel);
+extern void heap_storage_create(Relation rel);
extern Oid heap_create_with_catalog(char *relname, TupleDesc tupdesc,
char relkind, bool istemp,
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: index.h,v 1.29 2000/07/14 22:17:56 tgl Exp $
+ * $Id: index.h,v 1.30 2000/11/08 22:10:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Datum *datum,
char *nullv);
-extern void UpdateStats(Oid relid, long reltuples, bool inplace);
+extern void UpdateStats(Oid relid, long reltuples);
extern bool IndexesAreActive(Oid relid, bool comfirmCommitted);
-extern void setRelhasindexInplace(Oid relid, bool hasindex, bool immediate);
+extern void setRelhasindex(Oid relid, bool hasindex);
extern bool SetReindexProcessing(bool processing);
extern bool IsReindexProcessing(void);
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parse_clause.h,v 1.19 2000/09/12 21:07:12 tgl Exp $
+ * $Id: parse_clause.h,v 1.20 2000/11/08 22:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "parser/parse_node.h"
extern void makeRangeTable(ParseState *pstate, List *frmList);
+extern void lockTargetTable(ParseState *pstate, char *relname);
extern void setTargetTable(ParseState *pstate, char *relname,
bool inh, bool inJoinSet);
extern Node *transformWhereClause(ParseState *pstate, Node *where);
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parse_node.h,v 1.22 2000/09/29 18:21:40 tgl Exp $
+ * $Id: parse_node.h,v 1.23 2000/11/08 22:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
List *p_joinlist; /* join items so far (will become
* FromExpr node's fromlist) */
int p_last_resno; /* last targetlist resno assigned */
+ List *p_forUpdate; /* FOR UPDATE clause, if any (see gram.y) */
bool p_hasAggs;
bool p_hasSubLinks;
bool p_is_insert;
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: buf_internals.h,v 1.42 2000/10/28 16:21:00 vadim Exp $
+ * $Id: buf_internals.h,v 1.43 2000/11/08 22:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern BufferDesc *LocalBufferAlloc(Relation reln, BlockNumber blockNum,
bool *foundPtr);
extern int WriteLocalBuffer(Buffer buffer, bool release);
-extern int FlushLocalBuffer(Buffer buffer, bool release);
+extern int FlushLocalBuffer(Buffer buffer, bool sync, bool release);
extern void InitLocalBuffer(void);
extern void LocalBufferSync(void);
extern void ResetLocalBufferPool(void);
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: bufmgr.h,v 1.42 2000/10/28 16:21:00 vadim Exp $
+ * $Id: bufmgr.h,v 1.43 2000/11/08 22:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef BUFMGR_H
#define BUFMGR_H
-#include "storage/buf_internals.h"
#include "access/xlogdefs.h"
+#include "storage/buf_internals.h"
+#include "storage/relfilenode.h"
typedef void *Block;
extern int WriteNoReleaseBuffer(Buffer buffer);
extern Buffer ReleaseAndReadBuffer(Buffer buffer, Relation relation,
BlockNumber blockNum);
-extern int FlushBuffer(Buffer buffer, bool release);
+extern int FlushBuffer(Buffer buffer, bool sync, bool release);
extern void InitBufferPool(IPCKey key);
extern void PrintBufferUsage(FILE *statfp);
extern BlockNumber BufferGetBlockNumber(Buffer buffer);
extern BlockNumber RelationGetNumberOfBlocks(Relation relation);
extern int FlushRelationBuffers(Relation rel, BlockNumber firstDelBlock);
-extern void ReleaseRelationBuffers(Relation rel);
+extern void DropRelationBuffers(Relation rel);
+extern void DropRelFileNodeBuffers(RelFileNode rnode);
extern void DropBuffers(Oid dbid);
extern void PrintPinnedBufs(void);
extern int BufferShmemSize(void);
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: smgr.h,v 1.23 2000/10/28 16:21:00 vadim Exp $
+ * $Id: smgr.h,v 1.24 2000/11/08 22:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern int smgrcreate(int16 which, Relation reln);
extern int smgrunlink(int16 which, Relation reln);
extern int smgrextend(int16 which, Relation reln, char *buffer);
-extern int smgropen(int16 which, Relation reln);
+extern int smgropen(int16 which, Relation reln, bool failOK);
extern int smgrclose(int16 which, Relation reln);
extern int smgrread(int16 which, Relation reln, BlockNumber blocknum,
char *buffer);
extern int smgrmarkdirty(int16 which, Relation reln, BlockNumber blkno);
extern int smgrnblocks(int16 which, Relation reln);
extern int smgrtruncate(int16 which, Relation reln, int nblocks);
+extern int smgrDoPendingDeletes(bool isCommit);
extern int smgrcommit(void);
extern int smgrabort(void);
/* in md.c */
extern int mdinit(void);
extern int mdcreate(Relation reln);
-extern int mdunlink(Relation reln);
+extern int mdunlink(RelFileNode rnode);
extern int mdextend(Relation reln, char *buffer);
extern int mdopen(Relation reln);
extern int mdclose(Relation reln);
extern int mdwrite(Relation reln, BlockNumber blocknum, char *buffer);
extern int mdflush(Relation reln, BlockNumber blocknum, char *buffer);
extern int mdmarkdirty(Relation reln, BlockNumber blkno);
-extern int mdblindwrt(RelFileNode rnode, BlockNumber blkno,
- char *buffer, bool dofsync);
-extern int mdblindmarkdirty(RelFileNode rnode, BlockNumber blkno);
+extern int mdblindwrt(RelFileNode rnode, BlockNumber blkno,
+ char *buffer, bool dofsync);
+extern int mdblindmarkdirty(RelFileNode rnode, BlockNumber blkno);
extern int mdnblocks(Relation reln);
extern int mdtruncate(Relation reln, int nblocks);
extern int mdcommit(void);
extern int mminit(void);
extern int mmcreate(Relation reln);
-extern int mmunlink(Relation reln);
+extern int mmunlink(RelFileNode rnode);
extern int mmextend(Relation reln, char *buffer);
extern int mmopen(Relation reln);
extern int mmclose(Relation reln);
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: inval.h,v 1.17 2000/06/08 19:51:06 momjian Exp $
+ * $Id: inval.h,v 1.18 2000/11/08 22:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern void RelationMark4RollbackHeapTuple(Relation relation, HeapTuple tuple);
-extern void ImmediateInvalidateSharedHeapTuple(Relation relation, HeapTuple tuple);
-
#endif /* INVAL_H */
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: rel.h,v 1.41 2000/09/07 09:58:38 vadim Exp $
+ * $Id: rel.h,v 1.42 2000/11/08 22:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
uint16 rd_refcnt; /* reference count */
bool rd_myxactonly; /* rel uses the local buffer mgr */
bool rd_isnailed; /* rel is nailed in cache */
- bool rd_unlinked; /* rel already unlinked or not created yet */
bool rd_indexfound; /* true if rd_indexlist is valid */
bool rd_uniqueindex; /* true if rel is a UNIQUE index */
Form_pg_am rd_am; /* AM tuple */
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: temprel.h,v 1.11 2000/10/11 21:28:19 momjian Exp $
+ * $Id: temprel.h,v 1.12 2000/11/08 22:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
const char *newname);
extern void remove_all_temp_relations(void);
-extern void remove_temp_rel_in_myxid(void);
+extern void AtEOXact_temp_relations(bool isCommit);
extern char *get_temp_rel_by_username(const char *user_relname);
extern char *get_temp_rel_by_physicalname(const char *relname);
ERROR: parser: parse error at or near ";"
-- no such relation
alter table nonesuch rename to newnonesuch;
-ERROR: Relation 'nonesuch' does not exist
+ERROR: Relation "nonesuch" does not exist
-- no such relation
alter table nonesuch rename to stud_emp;
-ERROR: Relation 'nonesuch' does not exist
+ERROR: Relation "nonesuch" does not exist
-- system relation
alter table stud_emp rename to pg_stud_emp;
ERROR: renamerel: Illegal class name: "pg_stud_emp" -- pg_ is reserved for system catalogs