mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Avoid crashing when a table is deleted while we're on the process of checking
it. Per report from Tom Lane based on buildfarm evidence.
This commit is contained in:
		@@ -55,7 +55,7 @@
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * IDENTIFICATION
 | 
					 * IDENTIFICATION
 | 
				
			||||||
 *	  $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.80 2008/07/01 02:09:34 tgl Exp $
 | 
					 *	  $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.81 2008/07/17 21:02:31 alvherre Exp $
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *-------------------------------------------------------------------------
 | 
					 *-------------------------------------------------------------------------
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -176,6 +176,9 @@ typedef struct autovac_table
 | 
				
			|||||||
	int			at_vacuum_cost_delay;
 | 
						int			at_vacuum_cost_delay;
 | 
				
			||||||
	int			at_vacuum_cost_limit;
 | 
						int			at_vacuum_cost_limit;
 | 
				
			||||||
	bool		at_wraparound;
 | 
						bool		at_wraparound;
 | 
				
			||||||
 | 
						char	   *at_relname;
 | 
				
			||||||
 | 
						char	   *at_nspname;
 | 
				
			||||||
 | 
						char	   *at_datname;
 | 
				
			||||||
} autovac_table;
 | 
					} autovac_table;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*-------------
 | 
					/*-------------
 | 
				
			||||||
@@ -282,15 +285,13 @@ static void relation_needs_vacanalyze(Oid relid, Form_pg_autovacuum avForm,
 | 
				
			|||||||
						  PgStat_StatTabEntry *tabentry, bool *dovacuum,
 | 
											  PgStat_StatTabEntry *tabentry, bool *dovacuum,
 | 
				
			||||||
						  bool *doanalyze, bool *wraparound);
 | 
											  bool *doanalyze, bool *wraparound);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void autovacuum_do_vac_analyze(Oid relid, bool dovacuum,
 | 
					static void autovacuum_do_vac_analyze(autovac_table *tab,
 | 
				
			||||||
						  bool doanalyze, int freeze_min_age,
 | 
					 | 
				
			||||||
						  bool for_wraparound,
 | 
					 | 
				
			||||||
						  BufferAccessStrategy bstrategy);
 | 
											  BufferAccessStrategy bstrategy);
 | 
				
			||||||
static HeapTuple get_pg_autovacuum_tuple_relid(Relation avRel, Oid relid);
 | 
					static HeapTuple get_pg_autovacuum_tuple_relid(Relation avRel, Oid relid);
 | 
				
			||||||
static PgStat_StatTabEntry *get_pgstat_tabentry_relid(Oid relid, bool isshared,
 | 
					static PgStat_StatTabEntry *get_pgstat_tabentry_relid(Oid relid, bool isshared,
 | 
				
			||||||
						  PgStat_StatDBEntry *shared,
 | 
											  PgStat_StatDBEntry *shared,
 | 
				
			||||||
						  PgStat_StatDBEntry *dbentry);
 | 
											  PgStat_StatDBEntry *dbentry);
 | 
				
			||||||
static void autovac_report_activity(VacuumStmt *vacstmt, Oid relid);
 | 
					static void autovac_report_activity(autovac_table *tab);
 | 
				
			||||||
static void avl_sighup_handler(SIGNAL_ARGS);
 | 
					static void avl_sighup_handler(SIGNAL_ARGS);
 | 
				
			||||||
static void avl_sigusr1_handler(SIGNAL_ARGS);
 | 
					static void avl_sigusr1_handler(SIGNAL_ARGS);
 | 
				
			||||||
static void avl_sigterm_handler(SIGNAL_ARGS);
 | 
					static void avl_sigterm_handler(SIGNAL_ARGS);
 | 
				
			||||||
@@ -2061,9 +2062,6 @@ do_autovacuum(void)
 | 
				
			|||||||
		autovac_table *tab;
 | 
							autovac_table *tab;
 | 
				
			||||||
		WorkerInfo	worker;
 | 
							WorkerInfo	worker;
 | 
				
			||||||
		bool		skipit;
 | 
							bool		skipit;
 | 
				
			||||||
		char	   *datname,
 | 
					 | 
				
			||||||
				   *nspname,
 | 
					 | 
				
			||||||
				   *relname;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		CHECK_FOR_INTERRUPTS();
 | 
							CHECK_FOR_INTERRUPTS();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2158,13 +2156,17 @@ do_autovacuum(void)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * Save the relation name for a possible error message, to avoid a
 | 
							 * Save the relation name for a possible error message, to avoid a
 | 
				
			||||||
		 * catalog lookup in case of an error.	Note: they must live in a
 | 
							 * catalog lookup in case of an error.  If any of these return NULL,
 | 
				
			||||||
		 * long-lived memory context because we call vacuum and analyze in
 | 
							 * then the relation has been dropped since last we checked; skip it.
 | 
				
			||||||
		 * different transactions.
 | 
							 * Note: they must live in a long-lived memory context because we call
 | 
				
			||||||
 | 
							 * vacuum and analyze in different transactions.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		datname = get_database_name(MyDatabaseId);
 | 
					
 | 
				
			||||||
		nspname = get_namespace_name(get_rel_namespace(tab->at_relid));
 | 
							tab->at_relname = get_rel_name(tab->at_relid);
 | 
				
			||||||
		relname = get_rel_name(tab->at_relid);
 | 
							tab->at_nspname = get_namespace_name(get_rel_namespace(tab->at_relid));
 | 
				
			||||||
 | 
							tab->at_datname = get_database_name(MyDatabaseId);
 | 
				
			||||||
 | 
							if (!tab->at_relname || !tab->at_nspname || !tab->at_datname)
 | 
				
			||||||
 | 
								goto deleted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * We will abort vacuuming the current table if something errors out,
 | 
							 * We will abort vacuuming the current table if something errors out,
 | 
				
			||||||
@@ -2175,12 +2177,7 @@ do_autovacuum(void)
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			/* have at it */
 | 
								/* have at it */
 | 
				
			||||||
			MemoryContextSwitchTo(TopTransactionContext);
 | 
								MemoryContextSwitchTo(TopTransactionContext);
 | 
				
			||||||
			autovacuum_do_vac_analyze(tab->at_relid,
 | 
								autovacuum_do_vac_analyze(tab, bstrategy);
 | 
				
			||||||
									  tab->at_dovacuum,
 | 
					 | 
				
			||||||
									  tab->at_doanalyze,
 | 
					 | 
				
			||||||
									  tab->at_freeze_min_age,
 | 
					 | 
				
			||||||
									  tab->at_wraparound,
 | 
					 | 
				
			||||||
									  bstrategy);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/*
 | 
								/*
 | 
				
			||||||
			 * Clear a possible query-cancel signal, to avoid a late reaction
 | 
								 * Clear a possible query-cancel signal, to avoid a late reaction
 | 
				
			||||||
@@ -2199,10 +2196,10 @@ do_autovacuum(void)
 | 
				
			|||||||
			HOLD_INTERRUPTS();
 | 
								HOLD_INTERRUPTS();
 | 
				
			||||||
			if (tab->at_dovacuum)
 | 
								if (tab->at_dovacuum)
 | 
				
			||||||
				errcontext("automatic vacuum of table \"%s.%s.%s\"",
 | 
									errcontext("automatic vacuum of table \"%s.%s.%s\"",
 | 
				
			||||||
						   datname, nspname, relname);
 | 
											   tab->at_datname, tab->at_nspname, tab->at_relname);
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
				errcontext("automatic analyze of table \"%s.%s.%s\"",
 | 
									errcontext("automatic analyze of table \"%s.%s.%s\"",
 | 
				
			||||||
						   datname, nspname, relname);
 | 
											   tab->at_datname, tab->at_nspname, tab->at_relname);
 | 
				
			||||||
			EmitErrorReport();
 | 
								EmitErrorReport();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/* this resets the PGPROC flags too */
 | 
								/* this resets the PGPROC flags too */
 | 
				
			||||||
@@ -2219,10 +2216,14 @@ do_autovacuum(void)
 | 
				
			|||||||
		/* the PGPROC flags are reset at the next end of transaction */
 | 
							/* the PGPROC flags are reset at the next end of transaction */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* be tidy */
 | 
							/* be tidy */
 | 
				
			||||||
 | 
					deleted:
 | 
				
			||||||
 | 
							if (tab->at_datname != NULL)
 | 
				
			||||||
 | 
								pfree(tab->at_datname);
 | 
				
			||||||
 | 
							if (tab->at_nspname != NULL)
 | 
				
			||||||
 | 
								pfree(tab->at_nspname);
 | 
				
			||||||
 | 
							if (tab->at_relname != NULL)
 | 
				
			||||||
 | 
								pfree(tab->at_relname);
 | 
				
			||||||
		pfree(tab);
 | 
							pfree(tab);
 | 
				
			||||||
		pfree(datname);
 | 
					 | 
				
			||||||
		pfree(nspname);
 | 
					 | 
				
			||||||
		pfree(relname);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* remove my info from shared memory */
 | 
							/* remove my info from shared memory */
 | 
				
			||||||
		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
 | 
							LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
 | 
				
			||||||
@@ -2299,6 +2300,8 @@ get_pgstat_tabentry_relid(Oid relid, bool isshared, PgStat_StatDBEntry *shared,
 | 
				
			|||||||
 * Recheck whether a plain table still needs vacuum or analyze; be it because
 | 
					 * Recheck whether a plain table still needs vacuum or analyze; be it because
 | 
				
			||||||
 * it does directly, or because its TOAST table does.  Return value is a valid
 | 
					 * it does directly, or because its TOAST table does.  Return value is a valid
 | 
				
			||||||
 * autovac_table pointer if it does, NULL otherwise.
 | 
					 * autovac_table pointer if it does, NULL otherwise.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note that the returned autovac_table does not have the name fields set.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static autovac_table *
 | 
					static autovac_table *
 | 
				
			||||||
table_recheck_autovac(Oid relid)
 | 
					table_recheck_autovac(Oid relid)
 | 
				
			||||||
@@ -2437,6 +2440,9 @@ table_recheck_autovac(Oid relid)
 | 
				
			|||||||
		tab->at_vacuum_cost_limit = vac_cost_limit;
 | 
							tab->at_vacuum_cost_limit = vac_cost_limit;
 | 
				
			||||||
		tab->at_vacuum_cost_delay = vac_cost_delay;
 | 
							tab->at_vacuum_cost_delay = vac_cost_delay;
 | 
				
			||||||
		tab->at_wraparound = wraparound || toast_wraparound;
 | 
							tab->at_wraparound = wraparound || toast_wraparound;
 | 
				
			||||||
 | 
							tab->at_relname = NULL;
 | 
				
			||||||
 | 
							tab->at_nspname = NULL;
 | 
				
			||||||
 | 
							tab->at_datname = NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	heap_close(avRel, AccessShareLock);
 | 
						heap_close(avRel, AccessShareLock);
 | 
				
			||||||
@@ -2607,8 +2613,7 @@ relation_needs_vacanalyze(Oid relid,
 | 
				
			|||||||
 *		Vacuum and/or analyze the specified table
 | 
					 *		Vacuum and/or analyze the specified table
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
autovacuum_do_vac_analyze(Oid relid, bool dovacuum, bool doanalyze,
 | 
					autovacuum_do_vac_analyze(autovac_table *tab,
 | 
				
			||||||
						  int freeze_min_age, bool for_wraparound,
 | 
					 | 
				
			||||||
						  BufferAccessStrategy bstrategy)
 | 
											  BufferAccessStrategy bstrategy)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	VacuumStmt	vacstmt;
 | 
						VacuumStmt	vacstmt;
 | 
				
			||||||
@@ -2617,18 +2622,18 @@ autovacuum_do_vac_analyze(Oid relid, bool dovacuum, bool doanalyze,
 | 
				
			|||||||
	MemSet(&vacstmt, 0, sizeof(vacstmt));
 | 
						MemSet(&vacstmt, 0, sizeof(vacstmt));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vacstmt.type = T_VacuumStmt;
 | 
						vacstmt.type = T_VacuumStmt;
 | 
				
			||||||
	vacstmt.vacuum = dovacuum;
 | 
						vacstmt.vacuum = tab->at_dovacuum;
 | 
				
			||||||
	vacstmt.full = false;
 | 
						vacstmt.full = false;
 | 
				
			||||||
	vacstmt.analyze = doanalyze;
 | 
						vacstmt.analyze = tab->at_doanalyze;
 | 
				
			||||||
	vacstmt.freeze_min_age = freeze_min_age;
 | 
						vacstmt.freeze_min_age = tab->at_freeze_min_age;
 | 
				
			||||||
	vacstmt.verbose = false;
 | 
						vacstmt.verbose = false;
 | 
				
			||||||
	vacstmt.relation = NULL;	/* not used since we pass a relid */
 | 
						vacstmt.relation = NULL;	/* not used since we pass a relid */
 | 
				
			||||||
	vacstmt.va_cols = NIL;
 | 
						vacstmt.va_cols = NIL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Let pgstat know what we're doing */
 | 
						/* Let pgstat know what we're doing */
 | 
				
			||||||
	autovac_report_activity(&vacstmt, relid);
 | 
						autovac_report_activity(tab);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vacuum(&vacstmt, relid, bstrategy, for_wraparound, true);
 | 
						vacuum(&vacstmt, tab->at_relid, bstrategy, tab->at_wraparound, true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
@@ -2643,37 +2648,28 @@ autovacuum_do_vac_analyze(Oid relid, bool dovacuum, bool doanalyze,
 | 
				
			|||||||
 * bother to report "<IDLE>" or some such.
 | 
					 * bother to report "<IDLE>" or some such.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
autovac_report_activity(VacuumStmt *vacstmt, Oid relid)
 | 
					autovac_report_activity(autovac_table *tab)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char	   *relname = get_rel_name(relid);
 | 
					 | 
				
			||||||
	char	   *nspname = get_namespace_name(get_rel_namespace(relid));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define MAX_AUTOVAC_ACTIV_LEN (NAMEDATALEN * 2 + 32)
 | 
					#define MAX_AUTOVAC_ACTIV_LEN (NAMEDATALEN * 2 + 32)
 | 
				
			||||||
	char		activity[MAX_AUTOVAC_ACTIV_LEN];
 | 
						char	activity[MAX_AUTOVAC_ACTIV_LEN];
 | 
				
			||||||
 | 
						int		len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Report the command and possible options */
 | 
						/* Report the command and possible options */
 | 
				
			||||||
	if (vacstmt->vacuum)
 | 
						if (tab->at_dovacuum)
 | 
				
			||||||
		snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,
 | 
							snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,
 | 
				
			||||||
				 "autovacuum: VACUUM%s",
 | 
									 "autovacuum: VACUUM%s",
 | 
				
			||||||
				 vacstmt->analyze ? " ANALYZE" : "");
 | 
									 tab->at_doanalyze ? " ANALYZE" : "");
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,
 | 
							snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,
 | 
				
			||||||
				 "autovacuum: ANALYZE");
 | 
									 "autovacuum: ANALYZE");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Report the qualified name of the relation.
 | 
						 * Report the qualified name of the relation.
 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Paranoia is appropriate here in case relation was recently dropped ---
 | 
					 | 
				
			||||||
	 * the lsyscache routines we just invoked will return NULL rather than
 | 
					 | 
				
			||||||
	 * failing.
 | 
					 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (relname && nspname)
 | 
						len = strlen(activity);
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		int			len = strlen(activity);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		snprintf(activity + len, MAX_AUTOVAC_ACTIV_LEN - len,
 | 
						snprintf(activity + len, MAX_AUTOVAC_ACTIV_LEN - len,
 | 
				
			||||||
				 " %s.%s", nspname, relname);
 | 
								 " %s.%s", tab->at_nspname, tab->at_relname);
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Set statement_timestamp() to current time for pg_stat_activity */
 | 
						/* Set statement_timestamp() to current time for pg_stat_activity */
 | 
				
			||||||
	SetCurrentStatementStartTimestamp();
 | 
						SetCurrentStatementStartTimestamp();
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user