From c5451c22e38bc3044588c596966afcbe0c29b103 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 10 Nov 2008 00:49:37 +0000
Subject: [PATCH] Make relhasrules and relhastriggers work like relhasindex,
 namely we let VACUUM reset them to false rather than trying to clean 'em up
 during DROP.

---
 doc/src/sgml/catalogs.sgml          |  9 +++++++-
 src/backend/commands/analyze.c      | 12 +++++------
 src/backend/commands/vacuum.c       | 33 +++++++++++++++++++++++------
 src/backend/commands/vacuumlazy.c   | 15 ++++++-------
 src/backend/rewrite/rewriteRemove.c | 24 +++++++--------------
 src/backend/utils/cache/relcache.c  | 13 +++++++++++-
 src/include/commands/vacuum.h       |  4 ++--
 7 files changed, 67 insertions(+), 43 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ae9a6cfd8ec..33043fda3fc 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.181 2008/11/09 21:24:32 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.182 2008/11/10 00:49:36 tgl Exp $ -->
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
  -->
@@ -4478,6 +4478,13 @@
    </para>
   </note>
 
+  <note>
+   <para>
+    <literal>pg_class.relhastriggers</literal>
+    must be true if a table has any triggers in this catalog.
+   </para>
+  </note>
+
  </sect1>
 
 
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 13a66e17e98..6b95075be1f 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.127 2008/11/02 01:45:27 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.128 2008/11/10 00:49:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -463,10 +463,9 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
 	 */
 	if (!vacstmt->vacuum)
 	{
-		vac_update_relstats(RelationGetRelid(onerel),
+		vac_update_relstats(onerel,
 							RelationGetNumberOfBlocks(onerel),
-							totalrows, hasindex,
-							InvalidTransactionId);
+							totalrows, hasindex, InvalidTransactionId);
 
 		for (ind = 0; ind < nindexes; ind++)
 		{
@@ -474,10 +473,9 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
 			double		totalindexrows;
 
 			totalindexrows = ceil(thisdata->tupleFract * totalrows);
-			vac_update_relstats(RelationGetRelid(Irel[ind]),
+			vac_update_relstats(Irel[ind],
 								RelationGetNumberOfBlocks(Irel[ind]),
-								totalindexrows, false,
-								InvalidTransactionId);
+								totalindexrows, false, InvalidTransactionId);
 		}
 
 		/* report results to the stats collector, too */
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 14ed83d1d30..aa4c18915a2 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.379 2008/10/31 15:05:00 heikki Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.380 2008/11/10 00:49:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -664,6 +664,11 @@ vacuum_set_xid_limits(int freeze_min_age, bool sharedRel,
  *		pg_class would've been obsoleted.  Of course, this only works for
  *		fixed-size never-null columns, but these are.
  *
+ *		Note another assumption: that two VACUUMs/ANALYZEs on a table can't
+ *		run in parallel, nor can VACUUM/ANALYZE run in parallel with a
+ *		schema alteration such as adding an index, rule, or trigger.  Otherwise
+ *		our updates of relhasindex etc might overwrite uncommitted updates.
+ *
  *		Another reason for doing it this way is that when we are in a lazy
  *		VACUUM and have PROC_IN_VACUUM set, we mustn't do any updates ---
  *		somebody vacuuming pg_class might think they could delete a tuple
@@ -673,9 +678,11 @@ vacuum_set_xid_limits(int freeze_min_age, bool sharedRel,
  *		ANALYZE.
  */
 void
-vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples,
+vac_update_relstats(Relation relation,
+					BlockNumber num_pages, double num_tuples,
 					bool hasindex, TransactionId frozenxid)
 {
+	Oid			relid = RelationGetRelid(relation);
 	Relation	rd;
 	HeapTuple	ctup;
 	Form_pg_class pgcform;
@@ -724,6 +731,18 @@ vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples,
 		}
 	}
 
+	/* We also clear relhasrules and relhastriggers if needed */
+	if (pgcform->relhasrules && relation->rd_rules == NULL)
+	{
+		pgcform->relhasrules = false;
+		dirty = true;
+	}
+	if (pgcform->relhastriggers && relation->trigdesc == NULL)
+	{
+		pgcform->relhastriggers = false;
+		dirty = true;
+	}
+
 	/*
 	 * relfrozenxid should never go backward.  Caller can pass
 	 * InvalidTransactionId if it has no new data.
@@ -1269,9 +1288,9 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
 	FreeSpaceMapVacuum(onerel);
 
 	/* update statistics in pg_class */
-	vac_update_relstats(RelationGetRelid(onerel), vacrelstats->rel_pages,
-						vacrelstats->rel_tuples, vacrelstats->hasindex,
-						FreezeLimit);
+	vac_update_relstats(onerel,
+						vacrelstats->rel_pages, vacrelstats->rel_tuples,
+						vacrelstats->hasindex, FreezeLimit);
 
 	/* report results to the stats collector, too */
 	pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
@@ -3315,7 +3334,7 @@ scan_index(Relation indrel, double num_tuples)
 		return;
 
 	/* now update statistics in pg_class */
-	vac_update_relstats(RelationGetRelid(indrel),
+	vac_update_relstats(indrel,
 						stats->num_pages, stats->num_index_tuples,
 						false, InvalidTransactionId);
 
@@ -3385,7 +3404,7 @@ vacuum_index(VacPageList vacpagelist, Relation indrel,
 		return;
 
 	/* now update statistics in pg_class */
-	vac_update_relstats(RelationGetRelid(indrel),
+	vac_update_relstats(indrel,
 						stats->num_pages, stats->num_index_tuples,
 						false, InvalidTransactionId);
 
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 48be3f411af..246962a414a 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -29,7 +29,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.109 2008/10/31 15:05:00 heikki Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.110 2008/11/10 00:49:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -186,11 +186,9 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
 	FreeSpaceMapVacuum(onerel);
 
 	/* Update statistics in pg_class */
-	vac_update_relstats(RelationGetRelid(onerel),
-						vacrelstats->rel_pages,
-						vacrelstats->rel_tuples,
-						vacrelstats->hasindex,
-						FreezeLimit);
+	vac_update_relstats(onerel,
+						vacrelstats->rel_pages, vacrelstats->rel_tuples,
+						vacrelstats->hasindex, FreezeLimit);
 
 	/* report results to the stats collector, too */
 	pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
@@ -757,9 +755,8 @@ lazy_cleanup_index(Relation indrel,
 		return;
 
 	/* now update statistics in pg_class */
-	vac_update_relstats(RelationGetRelid(indrel),
-						stats->num_pages,
-						stats->num_index_tuples,
+	vac_update_relstats(indrel,
+						stats->num_pages, stats->num_index_tuples,
 						false, InvalidTransactionId);
 
 	ereport(elevel,
diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c
index c849296f2b3..8a39e489d9b 100644
--- a/src/backend/rewrite/rewriteRemove.c
+++ b/src/backend/rewrite/rewriteRemove.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteRemove.c,v 1.73 2008/06/19 00:46:05 alvherre Exp $
+ *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteRemove.c,v 1.74 2008/11/10 00:49:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,9 +22,9 @@
 #include "catalog/pg_rewrite.h"
 #include "miscadmin.h"
 #include "rewrite/rewriteRemove.h"
-#include "rewrite/rewriteSupport.h"
 #include "utils/acl.h"
 #include "utils/fmgroids.h"
+#include "utils/inval.h"
 #include "utils/lsyscache.h"
 #include "utils/rel.h"
 #include "utils/syscache.h"
@@ -103,7 +103,6 @@ RemoveRewriteRuleById(Oid ruleOid)
 	Relation	event_relation;
 	HeapTuple	tuple;
 	Oid			eventRelationOid;
-	bool		hasMoreRules;
 
 	/*
 	 * Open the pg_rewrite relation.
@@ -127,17 +126,13 @@ RemoveRewriteRuleById(Oid ruleOid)
 		elog(ERROR, "could not find tuple for rule %u", ruleOid);
 
 	/*
-	 * We had better grab AccessExclusiveLock so that we know no other rule
-	 * additions/deletions are going on for this relation.	Else we cannot set
-	 * relhasrules correctly.  Besides, we don't want to be changing the
-	 * ruleset while queries are executing on the rel.
+	 * We had better grab AccessExclusiveLock to ensure that no queries
+	 * are going on that might depend on this rule.  (Note: a weaker lock
+	 * would suffice if it's not an ON SELECT rule.)
 	 */
 	eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
 	event_relation = heap_open(eventRelationOid, AccessExclusiveLock);
 
-	hasMoreRules = event_relation->rd_rules != NULL &&
-		event_relation->rd_rules->numLocks > 1;
-
 	/*
 	 * Now delete the pg_rewrite tuple for the rule
 	 */
@@ -148,13 +143,10 @@ RemoveRewriteRuleById(Oid ruleOid)
 	heap_close(RewriteRelation, RowExclusiveLock);
 
 	/*
-	 * Set pg_class 'relhasrules' field correctly for event relation.
-	 *
-	 * Important side effect: an SI notice is broadcast to force all backends
-	 * (including me!) to update relcache entries with the new rule set.
-	 * Therefore, must do this even if relhasrules is still true!
+	 * Issue shared-inval notice to force all backends (including me!) to
+	 * update relcache entries with the new rule set.
 	 */
-	SetRelationRuleStatus(eventRelationOid, hasMoreRules, false);
+	CacheInvalidateRelcache(event_relation);
 
 	/* Close rel, but keep lock till commit... */
 	heap_close(event_relation, NoLock);
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 92adfd8300d..657ebf8c71f 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.275 2008/11/09 21:24:32 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.276 2008/11/10 00:49:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -721,6 +721,17 @@ RelationBuildRuleLock(Relation relation)
 	systable_endscan(rewrite_scan);
 	heap_close(rewrite_desc, AccessShareLock);
 
+	/*
+	 * there might not be any rules (if relhasrules is out-of-date)
+	 */
+	if (numlocks == 0)
+	{
+		relation->rd_rules = NULL;
+		relation->rd_rulescxt = NULL;
+		MemoryContextDelete(rulescxt);
+		return;
+	}
+
 	/*
 	 * form a RuleLock and insert into relation
 	 */
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index d45c3d0c96b..90446bc4f58 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/vacuum.h,v 1.80 2008/08/13 00:07:50 alvherre Exp $
+ * $PostgreSQL: pgsql/src/include/commands/vacuum.h,v 1.81 2008/11/10 00:49:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -130,7 +130,7 @@ extern void vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
 extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
 				 int *nindexes, Relation **Irel);
 extern void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode);
-extern void vac_update_relstats(Oid relid,
+extern void vac_update_relstats(Relation relation,
 					BlockNumber num_pages,
 					double num_tuples,
 					bool hasindex,
-- 
GitLab