From c66eb00adc59f319219f568a68e4c71da2c3fffb Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Thu, 13 Jun 2002 19:52:02 +0000 Subject: [PATCH] Allow ANALYZE to run in a transaction. --- src/backend/commands/analyze.c | 21 +---- src/backend/commands/vacuum.c | 168 ++++++++++++++++++--------------- 2 files changed, 93 insertions(+), 96 deletions(-) diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5785139a51..b3bfce9412 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -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(); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 953ff39714..8bb66f0799 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -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); + } /* -- 2.40.0