Allow ANALYZE to run in a transaction.
authorBruce Momjian
Thu, 13 Jun 2002 19:52:02 +0000 (19:52 +0000)
committerBruce Momjian
Thu, 13 Jun 2002 19:52:02 +0000 (19:52 +0000)
src/backend/commands/analyze.c
src/backend/commands/vacuum.c

index 5785139a514e256ff7c1d838f6ae8ffeb780d15a..b3bfce9412265d9fe0a351e008aeed54464c394e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.35 2002/05/24 18:57:55 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.36 2002/06/13 19:52:02 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -155,15 +155,6 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
    else
        elevel = DEBUG1;
 
-   /*
-    * Begin a transaction for analyzing this relation.
-    *
-    * Note: All memory allocated during ANALYZE will live in
-    * TransactionCommandContext or a subcontext thereof, so it will all
-    * be released by transaction commit at the end of this routine.
-    */
-   StartTransactionCommand();
-
    /*
     * Check for user-requested abort.  Note we want this to be inside a
     * transaction, so xact.c doesn't issue useless WARNING.
@@ -177,10 +168,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
    if (!SearchSysCacheExists(RELOID,
                              ObjectIdGetDatum(relid),
                              0, 0, 0))
-   {
-       CommitTransactionCommand();
        return;
-   }
 
    /*
     * Open the class, getting only a read lock on it, and check
@@ -196,7 +184,6 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
            elog(WARNING, "Skipping \"%s\" --- only table or database owner can ANALYZE it",
                 RelationGetRelationName(onerel));
        relation_close(onerel, AccessShareLock);
-       CommitTransactionCommand();
        return;
    }
 
@@ -211,7 +198,6 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
            elog(WARNING, "Skipping \"%s\" --- can not process indexes, views or special system tables",
                 RelationGetRelationName(onerel));
        relation_close(onerel, AccessShareLock);
-       CommitTransactionCommand();
        return;
    }
 
@@ -222,7 +208,6 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
        strcmp(RelationGetRelationName(onerel), StatisticRelationName) == 0)
    {
        relation_close(onerel, AccessShareLock);
-       CommitTransactionCommand();
        return;
    }
 
@@ -283,7 +268,6 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
    if (attr_cnt <= 0)
    {
        relation_close(onerel, NoLock);
-       CommitTransactionCommand();
        return;
    }
 
@@ -370,9 +354,6 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
     * entries we made in pg_statistic.)
     */
    relation_close(onerel, NoLock);
-
-   /* Commit and release working memory */
-   CommitTransactionCommand();
 }
 
 /*
index 953ff397147b2e088d4a94946a230b10fae958d1..8bb66f0799d330c95d6e6232cef21eaa1fd7635c 100644 (file)
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.226 2002/05/24 18:57:56 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.227 2002/06/13 19:52:02 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -110,8 +110,6 @@ static TransactionId initialFreezeLimit;
 
 
 /* non-export function prototypes */
-static void vacuum_init(VacuumStmt *vacstmt);
-static void vacuum_shutdown(VacuumStmt *vacstmt);
 static List *getrels(const RangeVar *vacrel, const char *stmttype);
 static void vac_update_dbstats(Oid dbid,
                   TransactionId vacuumXID,
@@ -160,6 +158,8 @@ static bool enough_space(VacPage vacpage, Size len);
 void
 vacuum(VacuumStmt *vacstmt)
 {
+   MemoryContext anl_context,
+                 old_context;
    const char *stmttype = vacstmt->vacuum ? "VACUUM" : "ANALYZE";
    List       *vrl,
               *cur;
@@ -178,13 +178,13 @@ vacuum(VacuumStmt *vacstmt)
     * user's transaction too, which would certainly not be the desired
     * behavior.
     */
-   if (IsTransactionBlock())
+   if (vacstmt->vacuum && IsTransactionBlock())
        elog(ERROR, "%s cannot run inside a BEGIN/END block", stmttype);
 
    /* Running VACUUM from a function would free the function context */
-   if (!MemoryContextContains(QueryContext, vacstmt))
+   if (vacstmt->vacuum && !MemoryContextContains(QueryContext, vacstmt))
        elog(ERROR, "%s cannot be executed from a function", stmttype);
-                        
+
    /*
     * Send info about dead objects to the statistics collector
     */
@@ -203,13 +203,62 @@ vacuum(VacuumStmt *vacstmt)
                                        ALLOCSET_DEFAULT_INITSIZE,
                                        ALLOCSET_DEFAULT_MAXSIZE);
 
+   if (vacstmt->analyze && !vacstmt->vacuum)
+       anl_context = AllocSetContextCreate(QueryContext,
+                                           "Analyze",
+                                           ALLOCSET_DEFAULT_MINSIZE,
+                                           ALLOCSET_DEFAULT_INITSIZE,
+                                           ALLOCSET_DEFAULT_MAXSIZE);
+
    /* Build list of relations to process (note this lives in vac_context) */
    vrl = getrels(vacstmt->relation, stmttype);
 
    /*
-    * Start up the vacuum cleaner.
+    *      Formerly, there was code here to prevent more than one VACUUM from
+    *      executing concurrently in the same database.  However, there's no
+    *      good reason to prevent that, and manually removing lockfiles after
+    *      a vacuum crash was a pain for dbadmins.  So, forget about lockfiles,
+    *      and just rely on the locks we grab on each target table
+    *      to ensure that there aren't two VACUUMs running on the same table
+    *      at the same time.
+    *
+    *      The strangeness with committing and starting transactions in the
+    *      init and shutdown routines is due to the fact that the vacuum cleaner
+    *      is invoked via an SQL command, and so is already executing inside
+    *      a transaction.  We need to leave ourselves in a predictable state
+    *      on entry and exit to the vacuum cleaner.  We commit the transaction
+    *      started in PostgresMain() inside vacuum_init(), and start one in
+    *      vacuum_shutdown() to match the commit waiting for us back in
+    *      PostgresMain().
     */
-   vacuum_init(vacstmt);
+   if (vacstmt->vacuum)
+   {
+       if (vacstmt->relation == NULL)
+       {
+           /*
+            * Compute the initially applicable OldestXmin and FreezeLimit
+            * XIDs, so that we can record these values at the end of the
+            * VACUUM. Note that individual tables may well be processed with
+            * newer values, but we can guarantee that no (non-shared)
+            * relations are processed with older ones.
+            *
+            * It is okay to record non-shared values in pg_database, even though
+            * we may vacuum shared relations with older cutoffs, because only
+            * the minimum of the values present in pg_database matters.  We
+            * can be sure that shared relations have at some time been
+            * vacuumed with cutoffs no worse than the global minimum; for, if
+            * there is a backend in some other DB with xmin = OLDXMIN that's
+            * determining the cutoff with which we vacuum shared relations,
+            * it is not possible for that database to have a cutoff newer
+            * than OLDXMIN recorded in pg_database.
+            */
+           vacuum_set_xid_limits(vacstmt, false,
+                                 &initialOldestXmin, &initialFreezeLimit);
+       }
+
+       /* matches the StartTransaction in PostgresMain() */
+       CommitTransactionCommand();
+   }
 
    /*
     * Process each selected relation.  We are careful to process each
@@ -225,81 +274,44 @@ vacuum(VacuumStmt *vacstmt)
        if (vacstmt->vacuum)
            vacuum_rel(relid, vacstmt, RELKIND_RELATION);
        if (vacstmt->analyze)
+       {
+           /* If we vacuumed, use new transaction for analyze. */
+           if (vacstmt->vacuum)
+               StartTransactionCommand();
+           else
+               old_context = MemoryContextSwitchTo(anl_context);
+
            analyze_rel(relid, vacstmt);
+
+           if (vacstmt->vacuum)
+               CommitTransactionCommand();
+           else
+           {
+               MemoryContextResetAndDeleteChildren(anl_context);
+               MemoryContextSwitchTo(old_context);
+           }
+       }
    }
 
    /* clean up */
-   vacuum_shutdown(vacstmt);
-}
-
-/*
- * vacuum_init(), vacuum_shutdown() -- start up and shut down the vacuum cleaner.
- *
- *     Formerly, there was code here to prevent more than one VACUUM from
- *     executing concurrently in the same database.  However, there's no
- *     good reason to prevent that, and manually removing lockfiles after
- *     a vacuum crash was a pain for dbadmins.  So, forget about lockfiles,
- *     and just rely on the locks we grab on each target table
- *     to ensure that there aren't two VACUUMs running on the same table
- *     at the same time.
- *
- *     The strangeness with committing and starting transactions in the
- *     init and shutdown routines is due to the fact that the vacuum cleaner
- *     is invoked via an SQL command, and so is already executing inside
- *     a transaction.  We need to leave ourselves in a predictable state
- *     on entry and exit to the vacuum cleaner.  We commit the transaction
- *     started in PostgresMain() inside vacuum_init(), and start one in
- *     vacuum_shutdown() to match the commit waiting for us back in
- *     PostgresMain().
- */
-static void
-vacuum_init(VacuumStmt *vacstmt)
-{
-   if (vacstmt->vacuum && vacstmt->relation == NULL)
+   if (vacstmt->vacuum)
    {
-       /*
-        * Compute the initially applicable OldestXmin and FreezeLimit
-        * XIDs, so that we can record these values at the end of the
-        * VACUUM. Note that individual tables may well be processed with
-        * newer values, but we can guarantee that no (non-shared)
-        * relations are processed with older ones.
-        *
-        * It is okay to record non-shared values in pg_database, even though
-        * we may vacuum shared relations with older cutoffs, because only
-        * the minimum of the values present in pg_database matters.  We
-        * can be sure that shared relations have at some time been
-        * vacuumed with cutoffs no worse than the global minimum; for, if
-        * there is a backend in some other DB with xmin = OLDXMIN that's
-        * determining the cutoff with which we vacuum shared relations,
-        * it is not possible for that database to have a cutoff newer
-        * than OLDXMIN recorded in pg_database.
-        */
-       vacuum_set_xid_limits(vacstmt, false,
-                             &initialOldestXmin, &initialFreezeLimit);
-   }
-
-   /* matches the StartTransaction in PostgresMain() */
-   CommitTransactionCommand();
-}
-
-static void
-vacuum_shutdown(VacuumStmt *vacstmt)
-{
-   /* on entry, we are not in a transaction */
+       /* on entry, we are not in a transaction */
 
-   /* matches the CommitTransaction in PostgresMain() */
-   StartTransactionCommand();
+       /* matches the CommitTransaction in PostgresMain() */
+       StartTransactionCommand();
 
-   /*
-    * If we did a database-wide VACUUM, update the database's pg_database
-    * row with info about the transaction IDs used, and try to truncate
-    * pg_clog.
-    */
-   if (vacstmt->vacuum && vacstmt->relation == NULL)
-   {
-       vac_update_dbstats(MyDatabaseId,
-                          initialOldestXmin, initialFreezeLimit);
-       vac_truncate_clog(initialOldestXmin, initialFreezeLimit);
+       /*
+        * If we did a database-wide VACUUM, update the database's pg_database
+        * row with info about the transaction IDs used, and try to truncate
+        * pg_clog.
+        */
+       if (vacstmt->relation == NULL)
+       {
+           vac_update_dbstats(MyDatabaseId,
+                              initialOldestXmin, initialFreezeLimit);
+           vac_truncate_clog(initialOldestXmin, initialFreezeLimit);
+       }
    }
 
    /*
@@ -309,6 +321,10 @@ vacuum_shutdown(VacuumStmt *vacstmt)
     */
    MemoryContextDelete(vac_context);
    vac_context = NULL;
+
+   if (vacstmt->analyze && !vacstmt->vacuum)
+       MemoryContextDelete(anl_context);
+
 }
 
 /*