From df3f5dfd194a837dc430c3a70bfc527b979935b3 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sun, 14 Jul 2002 21:08:08 +0000
Subject: [PATCH] In DeleteAttributeTuples, use a single indexscan instead of
 the multiple scans that will most likely be caused by SearchSysCache probes. 
 Also, share some code between index deletion and table deletion.

---
 src/backend/catalog/heap.c  | 337 ++++++++++++++++++------------------
 src/backend/catalog/index.c |  45 +----
 src/include/catalog/heap.h  |   5 +-
 3 files changed, 181 insertions(+), 206 deletions(-)

diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 207090ae70a..48f7cae1aa0 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.205 2002/07/12 18:43:13 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.206 2002/07/14 21:08:08 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -66,13 +66,11 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 					Relation new_rel_desc,
 					Oid new_rel_oid, Oid new_type_oid,
 					char relkind, bool relhasoids);
-static void DeleteAttributeTuples(Relation rel);
-static void DeleteRelationTuple(Relation rel);
-static void RelationRemoveInheritance(Relation relation);
 static void AddNewRelationType(const char *typeName,
 							   Oid typeNamespace,
 							   Oid new_rel_oid,
 							   Oid new_type_oid);
+static void RelationRemoveInheritance(Relation relation);
 static void StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin);
 static void StoreRelCheck(Relation rel, char *ccname, char *ccbin);
 static void StoreConstraints(Relation rel, TupleDesc tupdesc);
@@ -793,193 +791,72 @@ RelationRemoveInheritance(Relation relation)
 
 /*
  *		DeleteRelationTuple
+ *
+ * Remove pg_class row for the given relid.
+ *
+ * Note: this is shared by relation deletion and index deletion.  It's
+ * not intended for use anyplace else.
  */
-static void
-DeleteRelationTuple(Relation rel)
+void
+DeleteRelationTuple(Oid relid)
 {
 	Relation	pg_class_desc;
 	HeapTuple	tup;
 
-	/*
-	 * open pg_class
-	 */
+	/* Grab an appropriate lock on the pg_class relation */
 	pg_class_desc = heap_openr(RelationRelationName, RowExclusiveLock);
 
-	tup = SearchSysCacheCopy(RELOID,
-							 ObjectIdGetDatum(rel->rd_id),
-							 0, 0, 0);
+	tup = SearchSysCache(RELOID,
+						 ObjectIdGetDatum(relid),
+						 0, 0, 0);
 	if (!HeapTupleIsValid(tup))
-		elog(ERROR, "Relation \"%s\" does not exist",
-			 RelationGetRelationName(rel));
+		elog(ERROR, "DeleteRelationTuple: cache lookup failed for relation %u",
+			 relid);
 
-	/*
-	 * delete the relation tuple from pg_class, and finish up.
-	 */
+	/* delete the relation tuple from pg_class, and finish up */
 	simple_heap_delete(pg_class_desc, &tup->t_self);
-	heap_freetuple(tup);
 
-	heap_close(pg_class_desc, RowExclusiveLock);
-}
+	ReleaseSysCache(tup);
 
-/* --------------------------------
- * RelationTruncateIndexes - This routine is used to truncate all
- * indices associated with the heap relation to zero tuples.
- * The routine will truncate and then reconstruct the indices on
- * the relation specified by the heapId parameter.
- * --------------------------------
- */
-static void
-RelationTruncateIndexes(Oid heapId)
-{
-	Relation	indexRelation;
-	ScanKeyData entry;
-	SysScanDesc	scan;
-	HeapTuple	indexTuple;
-
-	/* Scan pg_index to find indexes on specified heap */
-	indexRelation = heap_openr(IndexRelationName, AccessShareLock);
-	ScanKeyEntryInitialize(&entry, 0,
-						   Anum_pg_index_indrelid,
-						   F_OIDEQ,
-						   ObjectIdGetDatum(heapId));
-	scan = systable_beginscan(indexRelation, IndexIndrelidIndex, true,
-							  SnapshotNow, 1, &entry);
-
-	while (HeapTupleIsValid(indexTuple = systable_getnext(scan)))
-	{
-		Form_pg_index indexform = (Form_pg_index) GETSTRUCT(indexTuple);
-		Oid			indexId;
-		IndexInfo  *indexInfo;
-		Relation	heapRelation,
-					currentIndex;
-
-		/*
-		 * For each index, fetch info needed for index_build
-		 */
-		indexId = indexform->indexrelid;
-		indexInfo = BuildIndexInfo(indexform);
-
-		/*
-		 * We have to re-open the heap rel each time through this loop
-		 * because index_build will close it again.  We need grab no lock,
-		 * however, because we assume heap_truncate is holding an
-		 * exclusive lock on the heap rel.
-		 */
-		heapRelation = heap_open(heapId, NoLock);
-
-		/* Open the index relation */
-		currentIndex = index_open(indexId);
-
-		/* Obtain exclusive lock on it, just to be sure */
-		LockRelation(currentIndex, AccessExclusiveLock);
-
-		/*
-		 * Drop any buffers associated with this index. If they're dirty,
-		 * they're just dropped without bothering to flush to disk.
-		 */
-		DropRelationBuffers(currentIndex);
-
-		/* Now truncate the actual data and set blocks to zero */
-		smgrtruncate(DEFAULT_SMGR, currentIndex, 0);
-		currentIndex->rd_nblocks = 0;
-		currentIndex->rd_targblock = InvalidBlockNumber;
-
-		/* Initialize the index and rebuild */
-		index_build(heapRelation, currentIndex, indexInfo);
-
-		/*
-		 * index_build will close both the heap and index relations (but
-		 * not give up the locks we hold on them).
-		 */
-	}
-
-	/* Complete the scan and close pg_index */
-	systable_endscan(scan);
-	heap_close(indexRelation, AccessShareLock);
+	heap_close(pg_class_desc, RowExclusiveLock);
 }
 
-/* ----------------------------
- *	 heap_truncate
+/*
+ *		DeleteAttributeTuples
  *
- *	 This routine is used to truncate the data from the
- *	 storage manager of any data within the relation handed
- *	 to this routine.
- * ----------------------------
+ * Remove pg_attribute rows for the given relid.
+ *
+ * Note: this is shared by relation deletion and index deletion.  It's
+ * not intended for use anyplace else.
  */
-
 void
-heap_truncate(Oid rid)
+DeleteAttributeTuples(Oid relid)
 {
-	Relation	rel;
-
-	/* Open relation for processing, and grab exclusive access on it. */
-
-	rel = heap_open(rid, AccessExclusiveLock);
-
-	/*
-	 * 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.  Disallow it except for
-	 * a rel created in the current xact (which would be deleted on abort,
-	 * anyway).
-	 */
-	if (IsTransactionBlock() && !rel->rd_myxactonly)
-		elog(ERROR, "TRUNCATE TABLE cannot run inside a transaction block");
-
-	/*
-	 * Release any buffers associated with this relation.  If they're
-	 * dirty, they're just dropped without bothering to flush to disk.
-	 */
-	DropRelationBuffers(rel);
-
-	/* Now truncate the actual data and set blocks to zero */
-	smgrtruncate(DEFAULT_SMGR, rel, 0);
-	rel->rd_nblocks = 0;
-	rel->rd_targblock = InvalidBlockNumber;
-
-	/* If this relation has indexes, truncate the indexes too */
-	RelationTruncateIndexes(rid);
-
-	/*
-	 * Close the relation, but keep exclusive lock on it until commit.
-	 */
-	heap_close(rel, NoLock);
-}
+	Relation	attrel;
+	SysScanDesc	scan;
+	ScanKeyData key[1];
+	HeapTuple	atttup;
 
+	/* Grab an appropriate lock on the pg_attribute relation */
+	attrel = heap_openr(AttributeRelationName, RowExclusiveLock);
 
-/* --------------------------------
- *		DeleteAttributeTuples
- *
- * --------------------------------
- */
-static void
-DeleteAttributeTuples(Relation rel)
-{
-	Relation	pg_attribute_desc;
-	HeapTuple	tup;
-	int2		attnum;
+	/* Use the index to scan only attributes of the target relation */
+	ScanKeyEntryInitialize(&key[0], 0x0,
+						   Anum_pg_attribute_attrelid, F_OIDEQ,
+						   ObjectIdGetDatum(relid));
 
-	/*
-	 * open pg_attribute
-	 */
-	pg_attribute_desc = heap_openr(AttributeRelationName, RowExclusiveLock);
+	scan = systable_beginscan(attrel, AttributeRelidNumIndex, true,
+							  SnapshotNow, 1, key);
 
-	for (attnum = FirstLowInvalidHeapAttributeNumber + 1;
-		 attnum <= rel->rd_att->natts;
-		 attnum++)
+	/* Delete all the matching tuples */
+	while ((atttup = systable_getnext(scan)) != NULL)
 	{
-		tup = SearchSysCacheCopy(ATTNUM,
-								 ObjectIdGetDatum(RelationGetRelid(rel)),
-								 Int16GetDatum(attnum),
-								 0, 0);
-		if (HeapTupleIsValid(tup))
-		{
-			simple_heap_delete(pg_attribute_desc, &tup->t_self);
-			heap_freetuple(tup);
-		}
+		simple_heap_delete(attrel, &atttup->t_self);
 	}
 
-	heap_close(pg_attribute_desc, RowExclusiveLock);
+	/* Clean up after the scan */
+	systable_endscan(scan);
+	heap_close(attrel, RowExclusiveLock);
 }
 
 /* ----------------------------------------------------------------
@@ -1033,14 +910,14 @@ heap_drop_with_catalog(Oid rid)
 	/*
 	 * delete attribute tuples and associated defaults
 	 */
-	DeleteAttributeTuples(rel);
+	DeleteAttributeTuples(RelationGetRelid(rel));
 
 	RemoveDefaults(rel);
 
 	/*
 	 * delete relation tuple
 	 */
-	DeleteRelationTuple(rel);
+	DeleteRelationTuple(RelationGetRelid(rel));
 
 	/*
 	 * unlink the relation's physical file and finish up.
@@ -1734,3 +1611,127 @@ RemoveStatistics(Relation rel)
 	heap_endscan(scan);
 	heap_close(pgstatistic, RowExclusiveLock);
 }
+
+
+/*
+ * RelationTruncateIndexes - truncate all
+ * indices associated with the heap relation to zero tuples.
+ *
+ * The routine will truncate and then reconstruct the indices on
+ * the relation specified by the heapId parameter.
+ */
+static void
+RelationTruncateIndexes(Oid heapId)
+{
+	Relation	indexRelation;
+	ScanKeyData entry;
+	SysScanDesc	scan;
+	HeapTuple	indexTuple;
+
+	/* Scan pg_index to find indexes on specified heap */
+	indexRelation = heap_openr(IndexRelationName, AccessShareLock);
+	ScanKeyEntryInitialize(&entry, 0,
+						   Anum_pg_index_indrelid,
+						   F_OIDEQ,
+						   ObjectIdGetDatum(heapId));
+	scan = systable_beginscan(indexRelation, IndexIndrelidIndex, true,
+							  SnapshotNow, 1, &entry);
+
+	while (HeapTupleIsValid(indexTuple = systable_getnext(scan)))
+	{
+		Form_pg_index indexform = (Form_pg_index) GETSTRUCT(indexTuple);
+		Oid			indexId;
+		IndexInfo  *indexInfo;
+		Relation	heapRelation,
+					currentIndex;
+
+		/*
+		 * For each index, fetch info needed for index_build
+		 */
+		indexId = indexform->indexrelid;
+		indexInfo = BuildIndexInfo(indexform);
+
+		/*
+		 * We have to re-open the heap rel each time through this loop
+		 * because index_build will close it again.  We need grab no lock,
+		 * however, because we assume heap_truncate is holding an
+		 * exclusive lock on the heap rel.
+		 */
+		heapRelation = heap_open(heapId, NoLock);
+
+		/* Open the index relation */
+		currentIndex = index_open(indexId);
+
+		/* Obtain exclusive lock on it, just to be sure */
+		LockRelation(currentIndex, AccessExclusiveLock);
+
+		/*
+		 * Drop any buffers associated with this index. If they're dirty,
+		 * they're just dropped without bothering to flush to disk.
+		 */
+		DropRelationBuffers(currentIndex);
+
+		/* Now truncate the actual data and set blocks to zero */
+		smgrtruncate(DEFAULT_SMGR, currentIndex, 0);
+		currentIndex->rd_nblocks = 0;
+		currentIndex->rd_targblock = InvalidBlockNumber;
+
+		/* Initialize the index and rebuild */
+		index_build(heapRelation, currentIndex, indexInfo);
+
+		/*
+		 * index_build will close both the heap and index relations (but
+		 * not give up the locks we hold on them).
+		 */
+	}
+
+	/* Complete the scan and close pg_index */
+	systable_endscan(scan);
+	heap_close(indexRelation, AccessShareLock);
+}
+
+/*
+ *	 heap_truncate
+ *
+ *	 This routine is used to truncate the data from the
+ *	 storage manager of any data within the relation handed
+ *	 to this routine.
+ */
+void
+heap_truncate(Oid rid)
+{
+	Relation	rel;
+
+	/* Open relation for processing, and grab exclusive access on it. */
+
+	rel = heap_open(rid, AccessExclusiveLock);
+
+	/*
+	 * 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.  Disallow it except for
+	 * a rel created in the current xact (which would be deleted on abort,
+	 * anyway).
+	 */
+	if (IsTransactionBlock() && !rel->rd_myxactonly)
+		elog(ERROR, "TRUNCATE TABLE cannot run inside a transaction block");
+
+	/*
+	 * Release any buffers associated with this relation.  If they're
+	 * dirty, they're just dropped without bothering to flush to disk.
+	 */
+	DropRelationBuffers(rel);
+
+	/* Now truncate the actual data and set blocks to zero */
+	smgrtruncate(DEFAULT_SMGR, rel, 0);
+	rel->rd_nblocks = 0;
+	rel->rd_targblock = InvalidBlockNumber;
+
+	/* If this relation has indexes, truncate the indexes too */
+	RelationTruncateIndexes(rid);
+
+	/*
+	 * Close the relation, but keep exclusive lock on it until commit.
+	 */
+	heap_close(rel, NoLock);
+}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 30cef89feff..996fb80a7cb 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.182 2002/07/12 18:43:13 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.183 2002/07/14 21:08:08 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -789,10 +789,7 @@ index_drop(Oid indexId)
 	Relation	userHeapRelation;
 	Relation	userIndexRelation;
 	Relation	indexRelation;
-	Relation	relationRelation;
-	Relation	attributeRelation;
 	HeapTuple	tuple;
-	int16		attnum;
 	int			i;
 
 	Assert(OidIsValid(indexId));
@@ -818,53 +815,27 @@ index_drop(Oid indexId)
 	/*
 	 * fix RELATION relation
 	 */
-	relationRelation = heap_openr(RelationRelationName, RowExclusiveLock);
-
-	/* Remove the pg_class tuple for the index itself */
-	tuple = SearchSysCacheCopy(RELOID,
-							   ObjectIdGetDatum(indexId),
-							   0, 0, 0);
-	if (!HeapTupleIsValid(tuple))
-		elog(ERROR, "index_drop: cache lookup failed for index %u",
-			 indexId);
-
-	simple_heap_delete(relationRelation, &tuple->t_self);
-	heap_freetuple(tuple);
-
-	heap_close(relationRelation, RowExclusiveLock);
-
+	DeleteRelationTuple(indexId);
 	/*
 	 * fix ATTRIBUTE relation
 	 */
-	attributeRelation = heap_openr(AttributeRelationName, RowExclusiveLock);
-
-	attnum = 1;					/* indexes start at 1 */
-
-	while (HeapTupleIsValid(tuple = SearchSysCacheCopy(ATTNUM,
-											   ObjectIdGetDatum(indexId),
-												   Int16GetDatum(attnum),
-													   0, 0)))
-	{
-		simple_heap_delete(attributeRelation, &tuple->t_self);
-		heap_freetuple(tuple);
-		attnum++;
-	}
-	heap_close(attributeRelation, RowExclusiveLock);
+	DeleteAttributeTuples(indexId);
 
 	/*
 	 * fix INDEX relation
 	 */
 	indexRelation = heap_openr(IndexRelationName, RowExclusiveLock);
 
-	tuple = SearchSysCacheCopy(INDEXRELID,
-							   ObjectIdGetDatum(indexId),
-							   0, 0, 0);
+	tuple = SearchSysCache(INDEXRELID,
+						   ObjectIdGetDatum(indexId),
+						   0, 0, 0);
 	if (!HeapTupleIsValid(tuple))
 		elog(ERROR, "index_drop: cache lookup failed for index %u",
 			 indexId);
 
 	simple_heap_delete(indexRelation, &tuple->t_self);
-	heap_freetuple(tuple);
+
+	ReleaseSysCache(tuple);
 	heap_close(indexRelation, RowExclusiveLock);
 
 	/*
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 5cf87e2631f..44c8e13bf61 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: heap.h,v 1.52 2002/07/12 18:43:19 tgl Exp $
+ * $Id: heap.h,v 1.53 2002/07/14 21:08:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -61,6 +61,9 @@ extern Node *cookDefault(ParseState *pstate,
 extern int	RemoveRelConstraints(Relation rel, const char *constrName,
 								 DropBehavior behavior);
 
+extern void DeleteRelationTuple(Oid relid);
+extern void DeleteAttributeTuples(Oid relid);
+
 extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno,
 						  bool relhasoids);
 
-- 
GitLab