diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index c13f602..caaae67 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5879,8 +5879,8 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
250ms then all automatic vacuums and analyzes that run
250ms or longer will be logged. In addition, when this parameter is
set to any value other than -1, a message will be
- logged if an autovacuum action is skipped due to the existence of a
- conflicting lock. Enabling this parameter can be helpful
+ logged if an autovacuum action is skipped due to a conflicting lock or a
+ concurrently dropped relation. Enabling this parameter can be helpful
in tracking autovacuum activity. This parameter can only be set in
the postgresql.conf> file or on the server command line;
but the setting can be overridden for individual tables by
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index d432f82..9e5c403 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -120,6 +120,7 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
int elevel;
AcquireSampleRowsFunc acquirefunc = NULL;
BlockNumber relpages = 0;
+ bool rel_lock;
/* Select logging level */
if (options & VACOPT_VERBOSE)
@@ -142,6 +143,7 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
* matter if we ever try to accumulate stats on dead tuples.) If the rel
* has been dropped since we last saw it, we don't need to process it.
*/
+ rel_lock = true;
if (!(options & VACOPT_NOWAIT))
onerel = try_relation_open(relid, ShareUpdateExclusiveLock);
else if (ConditionalLockRelationOid(relid, ShareUpdateExclusiveLock))
@@ -149,15 +151,51 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
else
{
onerel = NULL;
- if (relation &&
- IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
- ereport(LOG,
+ rel_lock = false;
+ }
+
+ /*
+ * If we failed to open or lock the relation, emit a log message before
+ * exiting.
+ */
+ if (!onerel)
+ {
+ /*
+ * If the RangeVar is not defined, we do not have enough
+ * information to provide a meaningful log statement. Chances
+ * are that analyze_rel's caller has intentionally not
+ * provided this information so that this logging is skipped,
+ * anyway.
+ */
+ if (relation == NULL)
+ return;
+
+ /*
+ * Determine the log level. For autovacuum logs, we emit a LOG
+ * if log_autovacuum_min_duration is not disabled. For manual
+ * ANALYZE, we emit a WARNING to match the log statements in
+ * the permissions checks.
+ */
+ if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
+ elevel = LOG;
+ else if (!IsAutoVacuumWorkerProcess())
+ elevel = WARNING;
+ else
+ return;
+
+ if (!rel_lock)
+ ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
- }
- if (!onerel)
+ else
+ ereport(elevel,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("skipping analyze of \"%s\" --- relation no longer exists",
+ relation->relname)));
+
return;
+ }
/*
* Check permissions --- this should match vacuum's check!
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index f439b55..96bcb0b 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -1327,6 +1327,7 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
Oid save_userid;
int save_sec_context;
int save_nestlevel;
+ bool rel_lock;
Assert(params != NULL);
@@ -1390,6 +1391,7 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
* If we've been asked not to wait for the relation lock, acquire it first
* in non-blocking mode, before calling try_relation_open().
*/
+ rel_lock = true;
if (!(options & VACOPT_NOWAIT))
onerel = try_relation_open(relid, lmode);
else if (ConditionalLockRelationOid(relid, lmode))
@@ -1397,16 +1399,58 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
else
{
onerel = NULL;
- if (relation &&
- IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
- ereport(LOG,
- (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("skipping vacuum of \"%s\" --- lock not available",
- relation->relname)));
+ rel_lock = false;
}
+ /*
+ * If we failed to open or lock the relation, emit a log message before
+ * exiting.
+ */
if (!onerel)
{
+ int elevel;
+
+ /*
+ * If the RangeVar is not defined, we do not have enough
+ * information to provide a meaningful log statement. Chances
+ * are that vacuum_rel's caller has intentionally not provided
+ * this information so that this logging is skipped, anyway.
+ */
+ if (relation == NULL)
+ {
+ PopActiveSnapshot();
+ CommitTransactionCommand();
+ return false;
+ }
+
+ /*
+ * Determine the log level. For autovacuum logs, we emit a LOG
+ * if log_autovacuum_min_duration is not disabled. For manual
+ * VACUUM, we emit a WARNING to match the log statements in the
+ * permissions checks.
+ */
+ if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
+ elevel = LOG;
+ else if (!IsAutoVacuumWorkerProcess())
+ elevel = WARNING;
+ else
+ {
+ PopActiveSnapshot();
+ CommitTransactionCommand();
+ return false;
+ }
+
+ if (!rel_lock)
+ ereport(elevel,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("skipping vacuum of \"%s\" --- lock not available",
+ relation->relname)));
+ else
+ ereport(elevel,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("skipping vacuum of \"%s\" --- relation no longer exists",
+ relation->relname)));
+
PopActiveSnapshot();
CommitTransactionCommand();
return false;
diff --git a/src/test/isolation/expected/vacuum-concurrent-drop.out b/src/test/isolation/expected/vacuum-concurrent-drop.out
new file mode 100644
index 0000000..72d80a1
--- /dev/null
+++ b/src/test/isolation/expected/vacuum-concurrent-drop.out
@@ -0,0 +1,76 @@
+Parsed test spec with 2 sessions
+
+starting permutation: lock vac_specified drop_and_commit
+step lock:
+ BEGIN;
+ LOCK test1 IN SHARE MODE;
+
+step vac_specified: VACUUM test1, test2;
+step drop_and_commit:
+ DROP TABLE test2;
+ COMMIT;
+
+WARNING: skipping vacuum of "test2" --- relation no longer exists
+step vac_specified: <... completed>
+
+starting permutation: lock vac_all drop_and_commit
+step lock:
+ BEGIN;
+ LOCK test1 IN SHARE MODE;
+
+step vac_all: VACUUM;
+step drop_and_commit:
+ DROP TABLE test2;
+ COMMIT;
+
+step vac_all: <... completed>
+
+starting permutation: lock analyze_specified drop_and_commit
+step lock:
+ BEGIN;
+ LOCK test1 IN SHARE MODE;
+
+step analyze_specified: ANALYZE test1, test2;
+step drop_and_commit:
+ DROP TABLE test2;
+ COMMIT;
+
+WARNING: skipping analyze of "test2" --- relation no longer exists
+step analyze_specified: <... completed>
+
+starting permutation: lock analyze_all drop_and_commit
+step lock:
+ BEGIN;
+ LOCK test1 IN SHARE MODE;
+
+step analyze_all: ANALYZE;
+step drop_and_commit:
+ DROP TABLE test2;
+ COMMIT;
+
+step analyze_all: <... completed>
+
+starting permutation: lock vac_analyze_specified drop_and_commit
+step lock:
+ BEGIN;
+ LOCK test1 IN SHARE MODE;
+
+step vac_analyze_specified: VACUUM ANALYZE test1, test2;
+step drop_and_commit:
+ DROP TABLE test2;
+ COMMIT;
+
+WARNING: skipping vacuum of "test2" --- relation no longer exists
+step vac_analyze_specified: <... completed>
+
+starting permutation: lock vac_analyze_all drop_and_commit
+step lock:
+ BEGIN;
+ LOCK test1 IN SHARE MODE;
+
+step vac_analyze_all: VACUUM ANALYZE;
+step drop_and_commit:
+ DROP TABLE test2;
+ COMMIT;
+
+step vac_analyze_all: <... completed>
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 7dad3c2..eb566eb 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -63,3 +63,4 @@ test: sequence-ddl
test: async-notify
test: vacuum-reltuples
test: timeouts
+test: vacuum-concurrent-drop
diff --git a/src/test/isolation/specs/vacuum-concurrent-drop.spec b/src/test/isolation/specs/vacuum-concurrent-drop.spec
new file mode 100644
index 0000000..31fc161
--- /dev/null
+++ b/src/test/isolation/specs/vacuum-concurrent-drop.spec
@@ -0,0 +1,45 @@
+# Test for log messages emitted by VACUUM and ANALYZE when a specified
+# relation is concurrently dropped.
+#
+# This also verifies that log messages are not emitted for concurrently
+# dropped relations that were not specified in the VACUUM or ANALYZE
+# command.
+
+setup
+{
+ CREATE TABLE test1 (a INT);
+ CREATE TABLE test2 (a INT);
+}
+
+teardown
+{
+ DROP TABLE IF EXISTS test1;
+ DROP TABLE IF EXISTS test2;
+}
+
+session "s1"
+step "lock"
+{
+ BEGIN;
+ LOCK test1 IN SHARE MODE;
+}
+step "drop_and_commit"
+{
+ DROP TABLE test2;
+ COMMIT;
+}
+
+session "s2"
+step "vac_specified" { VACUUM test1, test2; }
+step "vac_all" { VACUUM; }
+step "analyze_specified" { ANALYZE test1, test2; }
+step "analyze_all" { ANALYZE; }
+step "vac_analyze_specified" { VACUUM ANALYZE test1, test2; }
+step "vac_analyze_all" { VACUUM ANALYZE; }
+
+permutation "lock" "vac_specified" "drop_and_commit"
+permutation "lock" "vac_all" "drop_and_commit"
+permutation "lock" "analyze_specified" "drop_and_commit"
+permutation "lock" "analyze_all" "drop_and_commit"
+permutation "lock" "vac_analyze_specified" "drop_and_commit"
+permutation "lock" "vac_analyze_all" "drop_and_commit"