]> granicus.if.org Git - postgresql/commitdiff
Refactor relation opening for VACUUM and ANALYZE
authorMichael Paquier <michael@paquier.xyz>
Mon, 1 Oct 2018 23:53:38 +0000 (08:53 +0900)
committerMichael Paquier <michael@paquier.xyz>
Mon, 1 Oct 2018 23:53:38 +0000 (08:53 +0900)
VACUUM and ANALYZE share similar logic when it comes to opening a
relation to work on in terms of how the relation is opened, in which
order locks are tried and how logs should be generated when something
does not work as expected.

This commit refactors things so as both use the same code path to handle
the way a relation is opened, so as the integration of new options
becomes easier.

Author: Michael Paquier
Reviewed-by: Nathan Bossart
Discussion: https://postgr.es/m/20180927075152.GT1659@paquier.xyz

src/backend/commands/analyze.c
src/backend/commands/vacuum.c
src/include/commands/vacuum.h

index d11b559b20a382ac907c085a14921a4806bb9881..da757f0974784c47371dffc2807fe4dca0197736 100644 (file)
@@ -120,7 +120,6 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
        int                     elevel;
        AcquireSampleRowsFunc acquirefunc = NULL;
        BlockNumber relpages = 0;
-       bool            rel_lock = true;
 
        /* Select logging level */
        if (options & VACOPT_VERBOSE)
@@ -142,58 +141,16 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
         * concurrent VACUUM, which doesn't matter much at the moment but might
         * 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.
+        *
+        * Make sure to generate only logs for ANALYZE in this case.
         */
-       if (!(options & VACOPT_SKIP_LOCKED))
-               onerel = try_relation_open(relid, ShareUpdateExclusiveLock);
-       else if (ConditionalLockRelationOid(relid, ShareUpdateExclusiveLock))
-               onerel = try_relation_open(relid, NoLock);
-       else
-       {
-               onerel = NULL;
-               rel_lock = false;
-       }
+       onerel = vacuum_open_relation(relid, relation, params,
+                                                                 options & ~(VACOPT_VACUUM),
+                                                                 ShareUpdateExclusiveLock);
 
-       /*
-        * If we failed to open or lock the relation, emit a log message before
-        * exiting.
-        */
+       /* leave if relation could not be opened or locked */
        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())
-                       elevel = WARNING;
-               else if (params->log_min_duration >= 0)
-                       elevel = LOG;
-               else
-                       return;
-
-               if (!rel_lock)
-                       ereport(elevel,
-                                       (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
-                                        errmsg("skipping analyze of \"%s\" --- lock not available",
-                                                       relation->relname)));
-               else
-                       ereport(elevel,
-                                       (errcode(ERRCODE_UNDEFINED_TABLE),
-                                        errmsg("skipping analyze of \"%s\" --- relation no longer exists",
-                                                       relation->relname)));
-
                return;
-       }
 
        /*
         * Check if relation needs to be skipped based on ownership.  This check
index f1665097346c7568f0d65744672bb367df21d085..4e3823b0f0a93ed952ff0ac33c6ace7d111030b9 100644 (file)
@@ -482,6 +482,112 @@ vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple, int options)
 }
 
 
+/*
+ * vacuum_open_relation
+ *
+ * This routine is used for attempting to open and lock a relation which
+ * is going to be vacuumed or analyzed.  If the relation cannot be opened
+ * or locked, a log is emitted if possible.
+ */
+Relation
+vacuum_open_relation(Oid relid, RangeVar *relation, VacuumParams *params,
+                                        int options, LOCKMODE lmode)
+{
+       Relation        onerel;
+       bool            rel_lock = true;
+       int                     elevel;
+
+       Assert(params != NULL);
+       Assert((options & (VACOPT_VACUUM | VACOPT_ANALYZE)) != 0);
+
+       /*
+        * Open the relation and get the appropriate lock on it.
+        *
+        * There's a race condition here: the relation may have gone away since
+        * the last time we saw it.  If so, we don't need to vacuum or analyze it.
+        *
+        * If we've been asked not to wait for the relation lock, acquire it first
+        * in non-blocking mode, before calling try_relation_open().
+        */
+       if (!(options & VACOPT_SKIP_LOCKED))
+               onerel = try_relation_open(relid, lmode);
+       else if (ConditionalLockRelationOid(relid, lmode))
+               onerel = try_relation_open(relid, NoLock);
+       else
+       {
+               onerel = NULL;
+               rel_lock = false;
+       }
+
+       /* if relation is opened, leave */
+       if (onerel)
+               return onerel;
+
+       /*
+        * Relation could not be opened, hence generate if possible a log
+        * informing on the situation.
+        *
+        * If the RangeVar is not defined, we do not have enough information to
+        * provide a meaningful log statement.  Chances are that the caller has
+        * intentionally not provided this information so that this logging is
+        * skipped, anyway.
+        */
+       if (relation == NULL)
+               return NULL;
+
+       /*
+        * Determine the log level.
+        *
+        * For autovacuum logs, we emit a LOG if log_autovacuum_min_duration is
+        * not disabled.  For manual VACUUM or ANALYZE, we emit a WARNING to match
+        * the log statements in the permission checks.
+        */
+       if (!IsAutoVacuumWorkerProcess())
+               elevel = WARNING;
+       else if (params->log_min_duration >= 0)
+               elevel = LOG;
+       else
+               return NULL;
+
+       if ((options & VACOPT_VACUUM) != 0)
+       {
+               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)));
+
+               /*
+                * For VACUUM ANALYZE, both logs could show up, but just generate
+                * information for VACUUM as that would be the first one to be
+                * processed.
+                */
+               return NULL;
+       }
+
+       if ((options & VACOPT_ANALYZE) != 0)
+       {
+               if (!rel_lock)
+                       ereport(elevel,
+                                       (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+                                        errmsg("skipping analyze of \"%s\" --- lock not available",
+                                                       relation->relname)));
+               else
+                       ereport(elevel,
+                                       (errcode(ERRCODE_UNDEFINED_TABLE),
+                                        errmsg("skipping analyze of \"%s\" --- relation no longer exists",
+                                                       relation->relname)));
+       }
+
+       return NULL;
+}
+
+
 /*
  * Given a VacuumRelation, fill in the table OID if it wasn't specified,
  * and optionally add VacuumRelations for partitions of the table.
@@ -1400,7 +1506,6 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
        Oid                     save_userid;
        int                     save_sec_context;
        int                     save_nestlevel;
-       bool            rel_lock = true;
 
        Assert(params != NULL);
 
@@ -1455,68 +1560,12 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
         */
        lmode = (options & VACOPT_FULL) ? AccessExclusiveLock : ShareUpdateExclusiveLock;
 
-       /*
-        * Open the relation and get the appropriate lock on it.
-        *
-        * There's a race condition here: the rel may have gone away since the
-        * last time we saw it.  If so, we don't need to vacuum it.
-        *
-        * If we've been asked not to wait for the relation lock, acquire it first
-        * in non-blocking mode, before calling try_relation_open().
-        */
-       if (!(options & VACOPT_SKIP_LOCKED))
-               onerel = try_relation_open(relid, lmode);
-       else if (ConditionalLockRelationOid(relid, lmode))
-               onerel = try_relation_open(relid, NoLock);
-       else
-       {
-               onerel = NULL;
-               rel_lock = false;
-       }
+       /* open the relation and get the appropriate lock on it */
+       onerel = vacuum_open_relation(relid, relation, params, options, lmode);
 
-       /*
-        * If we failed to open or lock the relation, emit a log message before
-        * exiting.
-        */
+       /* leave if relation could not be opened or locked */
        if (!onerel)
        {
-               int                     elevel = 0;
-
-               /*
-                * Determine the log level.
-                *
-                * 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.
-                *
-                * Otherwise, 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 permission
-                * checks.
-                */
-               if (relation != NULL)
-               {
-                       if (!IsAutoVacuumWorkerProcess())
-                               elevel = WARNING;
-                       else if (params->log_min_duration >= 0)
-                               elevel = LOG;
-               }
-
-               if (elevel != 0)
-               {
-                       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;
index 5af96fdc8a2e8b8c5bd6618a5df0f94159833929..2f4303e40d83eedd53fe3b1ee12848d427345fe6 100644 (file)
@@ -188,6 +188,8 @@ extern void vac_update_datfrozenxid(void);
 extern void vacuum_delay_point(void);
 extern bool vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple,
                                                 int options);
+extern Relation vacuum_open_relation(Oid relid, RangeVar *relation,
+                                        VacuumParams *params, int options, LOCKMODE lmode);
 
 /* in commands/vacuumlazy.c */
 extern void lazy_vacuum_rel(Relation onerel, int options,