]> granicus.if.org Git - postgresql/blobdiff - src/backend/commands/vacuum.c
Fix "ANALYZE t, t" inside a transaction block.
[postgresql] / src / backend / commands / vacuum.c
index be03185a7f7d7d4d813ce18d0efe764cb7d1b856..7d6c50b49d9301bbacdbfd280d5633a23cc91c93 100644 (file)
@@ -36,6 +36,7 @@
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_namespace.h"
 #include "commands/cluster.h"
+#include "commands/defrem.h"
 #include "commands/vacuum.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -71,10 +72,11 @@ static BufferAccessStrategy vac_strategy;
 static List *expand_vacuum_rel(VacuumRelation *vrel, int options);
 static List *get_all_vacuum_rels(int options);
 static void vac_truncate_clog(TransactionId frozenXID,
-                                 MultiXactId minMulti,
-                                 TransactionId lastSaneFrozenXid,
-                                 MultiXactId lastSaneMinMulti);
+                                                         MultiXactId minMulti,
+                                                         TransactionId lastSaneFrozenXid,
+                                                         MultiXactId lastSaneMinMulti);
 static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params);
+static VacOptTernaryValue get_vacopt_ternary_value(DefElem *def);
 
 /*
  * Primary entry point for manual VACUUM and ANALYZE commands
@@ -83,20 +85,77 @@ static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params);
  * happen in vacuum().
  */
 void
-ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
+ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 {
        VacuumParams params;
+       bool            verbose = false;
+       bool            skip_locked = false;
+       bool            analyze = false;
+       bool            freeze = false;
+       bool            full = false;
+       bool            disable_page_skipping = false;
+       ListCell   *lc;
+
+       /* Set default value */
+       params.index_cleanup = VACOPT_TERNARY_DEFAULT;
+       params.truncate = VACOPT_TERNARY_DEFAULT;
+
+       /* Parse options list */
+       foreach(lc, vacstmt->options)
+       {
+               DefElem    *opt = (DefElem *) lfirst(lc);
+
+               /* Parse common options for VACUUM and ANALYZE */
+               if (strcmp(opt->defname, "verbose") == 0)
+                       verbose = defGetBoolean(opt);
+               else if (strcmp(opt->defname, "skip_locked") == 0)
+                       skip_locked = defGetBoolean(opt);
+               else if (!vacstmt->is_vacuumcmd)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("unrecognized ANALYZE option \"%s\"", opt->defname),
+                                        parser_errposition(pstate, opt->location)));
+
+               /* Parse options available on VACUUM */
+               else if (strcmp(opt->defname, "analyze") == 0)
+                       analyze = defGetBoolean(opt);
+               else if (strcmp(opt->defname, "freeze") == 0)
+                       freeze = defGetBoolean(opt);
+               else if (strcmp(opt->defname, "full") == 0)
+                       full = defGetBoolean(opt);
+               else if (strcmp(opt->defname, "disable_page_skipping") == 0)
+                       disable_page_skipping = defGetBoolean(opt);
+               else if (strcmp(opt->defname, "index_cleanup") == 0)
+                       params.index_cleanup = get_vacopt_ternary_value(opt);
+               else if (strcmp(opt->defname, "truncate") == 0)
+                       params.truncate = get_vacopt_ternary_value(opt);
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("unrecognized VACUUM option \"%s\"", opt->defname),
+                                        parser_errposition(pstate, opt->location)));
+       }
+
+       /* Set vacuum options */
+       params.options =
+               (vacstmt->is_vacuumcmd ? VACOPT_VACUUM : VACOPT_ANALYZE) |
+               (verbose ? VACOPT_VERBOSE : 0) |
+               (skip_locked ? VACOPT_SKIP_LOCKED : 0) |
+               (analyze ? VACOPT_ANALYZE : 0) |
+               (freeze ? VACOPT_FREEZE : 0) |
+               (full ? VACOPT_FULL : 0) |
+               (disable_page_skipping ? VACOPT_DISABLE_PAGE_SKIPPING : 0);
 
        /* sanity checks on options */
-       Assert(vacstmt->options & (VACOPT_VACUUM | VACOPT_ANALYZE));
-       Assert((vacstmt->options & VACOPT_VACUUM) ||
-                  !(vacstmt->options & (VACOPT_FULL | VACOPT_FREEZE)));
-       Assert(!(vacstmt->options & VACOPT_SKIPTOAST));
+       Assert(params.options & (VACOPT_VACUUM | VACOPT_ANALYZE));
+       Assert((params.options & VACOPT_VACUUM) ||
+                  !(params.options & (VACOPT_FULL | VACOPT_FREEZE)));
+       Assert(!(params.options & VACOPT_SKIPTOAST));
 
        /*
         * Make sure VACOPT_ANALYZE is specified if any column lists are present.
         */
-       if (!(vacstmt->options & VACOPT_ANALYZE))
+       if (!(params.options & VACOPT_ANALYZE))
        {
                ListCell   *lc;
 
@@ -111,14 +170,11 @@ ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
                }
        }
 
-       /* copy options from parse tree */
-       params.options = vacstmt->options;
-
        /*
         * All freeze ages are zero if the FREEZE option is given; otherwise pass
         * them as -1 which means to use the default values.
         */
-       if (vacstmt->options & VACOPT_FREEZE)
+       if (params.options & VACOPT_FREEZE)
        {
                params.freeze_min_age = 0;
                params.freeze_table_age = 0;
@@ -362,6 +418,15 @@ vacuum(List *relations, VacuumParams *params,
                                        PopActiveSnapshot();
                                        CommitTransactionCommand();
                                }
+                               else
+                               {
+                                       /*
+                                        * If we're not using separate xacts, better separate the
+                                        * ANALYZE actions with CCIs.  This avoids trouble if user
+                                        * says "ANALYZE t, t".
+                                        */
+                                       CommandCounterIncrement();
+                               }
                        }
                }
        }
@@ -537,8 +602,9 @@ vacuum_open_relation(Oid relid, RangeVar *relation, int options,
        /*
         * Determine the log level.
         *
-        * For manual VACUUM or ANALYZE, we emit a WARNING to match the log statements
-        * in the permission checks; otherwise, only log if the caller so requested.
+        * For manual VACUUM or ANALYZE, we emit a WARNING to match the log
+        * statements in the permission checks; otherwise, only log if the caller
+        * so requested.
         */
        if (!IsAutoVacuumWorkerProcess())
                elevel = WARNING;
@@ -790,7 +856,7 @@ get_all_vacuum_rels(int options)
 }
 
 /*
- * vacuum_set_xid_limits() -- compute oldest-Xmin and freeze cutoff points
+ * vacuum_set_xid_limits() -- compute oldestXmin and freeze cutoff points
  *
  * The output parameters are:
  * - oldestXmin is the cutoff value used to distinguish whether tuples are
@@ -1260,36 +1326,61 @@ vac_update_datfrozenxid(void)
 
                /*
                 * Only consider relations able to hold unfrozen XIDs (anything else
-                * should have InvalidTransactionId in relfrozenxid anyway.)
+                * should have InvalidTransactionId in relfrozenxid anyway).
                 */
                if (classForm->relkind != RELKIND_RELATION &&
                        classForm->relkind != RELKIND_MATVIEW &&
                        classForm->relkind != RELKIND_TOASTVALUE)
+               {
+                       Assert(!TransactionIdIsValid(classForm->relfrozenxid));
+                       Assert(!MultiXactIdIsValid(classForm->relminmxid));
                        continue;
-
-               Assert(TransactionIdIsNormal(classForm->relfrozenxid));
-               Assert(MultiXactIdIsValid(classForm->relminmxid));
+               }
 
                /*
+                * Some table AMs might not need per-relation xid / multixid horizons.
+                * It therefore seems reasonable to allow relfrozenxid and relminmxid
+                * to not be set (i.e. set to their respective Invalid*Id)
+                * independently. Thus validate and compute horizon for each only if
+                * set.
+                *
                 * If things are working properly, no relation should have a
                 * relfrozenxid or relminmxid that is "in the future".  However, such
                 * cases have been known to arise due to bugs in pg_upgrade.  If we
                 * see any entries that are "in the future", chicken out and don't do
-                * anything.  This ensures we won't truncate clog before those
-                * relations have been scanned and cleaned up.
+                * anything.  This ensures we won't truncate clog & multixact SLRUs
+                * before those relations have been scanned and cleaned up.
                 */
-               if (TransactionIdPrecedes(lastSaneFrozenXid, classForm->relfrozenxid) ||
-                       MultiXactIdPrecedes(lastSaneMinMulti, classForm->relminmxid))
+
+               if (TransactionIdIsValid(classForm->relfrozenxid))
                {
-                       bogus = true;
-                       break;
+                       Assert(TransactionIdIsNormal(classForm->relfrozenxid));
+
+                       /* check for values in the future */
+                       if (TransactionIdPrecedes(lastSaneFrozenXid, classForm->relfrozenxid))
+                       {
+                               bogus = true;
+                               break;
+                       }
+
+                       /* determine new horizon */
+                       if (TransactionIdPrecedes(classForm->relfrozenxid, newFrozenXid))
+                               newFrozenXid = classForm->relfrozenxid;
                }
 
-               if (TransactionIdPrecedes(classForm->relfrozenxid, newFrozenXid))
-                       newFrozenXid = classForm->relfrozenxid;
+               if (MultiXactIdIsValid(classForm->relminmxid))
+               {
+                       /* check for values in the future */
+                       if (MultiXactIdPrecedes(lastSaneMinMulti, classForm->relminmxid))
+                       {
+                               bogus = true;
+                               break;
+                       }
 
-               if (MultiXactIdPrecedes(classForm->relminmxid, newMinMulti))
-                       newMinMulti = classForm->relminmxid;
+                       /* determine new horizon */
+                       if (MultiXactIdPrecedes(classForm->relminmxid, newMinMulti))
+                               newMinMulti = classForm->relminmxid;
+               }
        }
 
        /* we're done with pg_class */
@@ -1672,6 +1763,26 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
        onerelid = onerel->rd_lockInfo.lockRelId;
        LockRelationIdForSession(&onerelid, lmode);
 
+       /* Set index cleanup option based on reloptions if not yet */
+       if (params->index_cleanup == VACOPT_TERNARY_DEFAULT)
+       {
+               if (onerel->rd_options == NULL ||
+                       ((StdRdOptions *) onerel->rd_options)->vacuum_index_cleanup)
+                       params->index_cleanup = VACOPT_TERNARY_ENABLED;
+               else
+                       params->index_cleanup = VACOPT_TERNARY_DISABLED;
+       }
+
+       /* Set truncate option based on reloptions if not yet */
+       if (params->truncate == VACOPT_TERNARY_DEFAULT)
+       {
+               if (onerel->rd_options == NULL ||
+                       ((StdRdOptions *) onerel->rd_options)->vacuum_truncate)
+                       params->truncate = VACOPT_TERNARY_ENABLED;
+               else
+                       params->truncate = VACOPT_TERNARY_DISABLED;
+       }
+
        /*
         * Remember the relation's TOAST relation for later, if the caller asked
         * us to process it.  In VACUUM FULL, though, the toast table is
@@ -1711,7 +1822,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
                cluster_rel(relid, InvalidOid, cluster_options);
        }
        else
-               heap_vacuum_rel(onerel, params, vac_strategy);
+               table_relation_vacuum(onerel, params, vac_strategy);
 
        /* Roll back any GUC changes executed by index functions */
        AtEOXact_GUC(false, save_nestlevel);
@@ -1852,3 +1963,15 @@ vacuum_delay_point(void)
                CHECK_FOR_INTERRUPTS();
        }
 }
+
+/*
+ * A wrapper function of defGetBoolean().
+ *
+ * This function returns VACOPT_TERNARY_ENABLED and VACOPT_TERNARY_DISABLED
+ * instead of true and false.
+ */
+static VacOptTernaryValue
+get_vacopt_ternary_value(DefElem *def)
+{
+       return defGetBoolean(def) ? VACOPT_TERNARY_ENABLED : VACOPT_TERNARY_DISABLED;
+}