1313 *
1414 *
1515 * IDENTIFICATION
16- * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.276 2004/05/21 16:08:46 tgl Exp $
16+ * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.277 2004/05/22 23:14:38 tgl Exp $
1717 *
1818 *-------------------------------------------------------------------------
1919 */
@@ -161,7 +161,9 @@ vacuum(VacuumStmt *vacstmt)
161161 MemoryContext anl_context = NULL ;
162162 TransactionId initialOldestXmin = InvalidTransactionId ;
163163 TransactionId initialFreezeLimit = InvalidTransactionId ;
164- bool all_rels ;
164+ bool all_rels ,
165+ in_outer_xact ,
166+ use_own_xacts ;
165167 List * relations ,
166168 * cur ;
167169
@@ -177,10 +179,23 @@ vacuum(VacuumStmt *vacstmt)
177179 * Furthermore, the forced commit that occurs before truncating the
178180 * relation's file would have the effect of committing the rest of the
179181 * user's transaction too, which would certainly not be the desired
180- * behavior.
182+ * behavior. (This only applies to VACUUM FULL, though. We could
183+ * in theory run lazy VACUUM inside a transaction block, but we choose
184+ * to disallow that case because we'd rather commit as soon as possible
185+ * after finishing the vacuum. This is mainly so that we can let go the
186+ * AccessExclusiveLock that we may be holding.)
187+ *
188+ * ANALYZE (without VACUUM) can run either way.
181189 */
182190 if (vacstmt -> vacuum )
191+ {
183192 PreventTransactionChain ((void * ) vacstmt , stmttype );
193+ in_outer_xact = false;
194+ }
195+ else
196+ {
197+ in_outer_xact = IsInTransactionChain ((void * ) vacstmt );
198+ }
184199
185200 /* Turn vacuum cost accounting on or off */
186201 VacuumCostActive = (VacuumCostNaptime > 0 );
@@ -205,81 +220,89 @@ vacuum(VacuumStmt *vacstmt)
205220 ALLOCSET_DEFAULT_INITSIZE ,
206221 ALLOCSET_DEFAULT_MAXSIZE );
207222
208- /*
209- * If we are running only ANALYZE, we don't need per-table
210- * transactions, but we still need a memory context with table
211- * lifetime.
212- */
213- if (vacstmt -> analyze && !vacstmt -> vacuum )
214- anl_context = AllocSetContextCreate (PortalContext ,
215- "Analyze" ,
216- ALLOCSET_DEFAULT_MINSIZE ,
217- ALLOCSET_DEFAULT_INITSIZE ,
218- ALLOCSET_DEFAULT_MAXSIZE );
219-
220223 /* Assume we are processing everything unless one table is mentioned */
221224 all_rels = (vacstmt -> relation == NULL );
222225
223226 /* Build list of relations to process (note this lives in vac_context) */
224227 relations = get_rel_oids (vacstmt -> relation , stmttype );
225228
229+ if (vacstmt -> vacuum && all_rels )
230+ {
231+ /*
232+ * It's a database-wide VACUUM.
233+ *
234+ * Compute the initially applicable OldestXmin and FreezeLimit
235+ * XIDs, so that we can record these values at the end of the
236+ * VACUUM. Note that individual tables may well be processed
237+ * with newer values, but we can guarantee that no
238+ * (non-shared) relations are processed with older ones.
239+ *
240+ * It is okay to record non-shared values in pg_database, even
241+ * though we may vacuum shared relations with older cutoffs,
242+ * because only the minimum of the values present in
243+ * pg_database matters. We can be sure that shared relations
244+ * have at some time been vacuumed with cutoffs no worse than
245+ * the global minimum; for, if there is a backend in some
246+ * other DB with xmin = OLDXMIN that's determining the cutoff
247+ * with which we vacuum shared relations, it is not possible
248+ * for that database to have a cutoff newer than OLDXMIN
249+ * recorded in pg_database.
250+ */
251+ vacuum_set_xid_limits (vacstmt , false,
252+ & initialOldestXmin ,
253+ & initialFreezeLimit );
254+ }
255+
226256 /*
227- * Formerly, there was code here to prevent more than one VACUUM from
228- * executing concurrently in the same database. However, there's no
229- * good reason to prevent that, and manually removing lockfiles after
230- * a vacuum crash was a pain for dbadmins. So, forget about
231- * lockfiles, and just rely on the locks we grab on each target table
232- * to ensure that there aren't two VACUUMs running on the same table
233- * at the same time.
257+ * Decide whether we need to start/commit our own transactions.
258+ *
259+ * For VACUUM (with or without ANALYZE): always do so, so that we
260+ * can release locks as soon as possible. (We could possibly use the
261+ * outer transaction for a one-table VACUUM, but handling TOAST tables
262+ * would be problematic.)
263+ *
264+ * For ANALYZE (no VACUUM): if inside a transaction block, we cannot
265+ * start/commit our own transactions. Also, there's no need to do so
266+ * if only processing one relation. For multiple relations when not
267+ * within a transaction block, use own transactions so we can release
268+ * locks sooner.
234269 */
270+ if (vacstmt -> vacuum )
271+ {
272+ use_own_xacts = true;
273+ }
274+ else
275+ {
276+ Assert (vacstmt -> analyze );
277+ if (in_outer_xact )
278+ use_own_xacts = false;
279+ else if (length (relations ) > 1 )
280+ use_own_xacts = true;
281+ else
282+ use_own_xacts = false;
283+ }
284+
285+ /*
286+ * If we are running ANALYZE without per-table transactions, we'll
287+ * need a memory context with table lifetime.
288+ */
289+ if (!use_own_xacts )
290+ anl_context = AllocSetContextCreate (PortalContext ,
291+ "Analyze" ,
292+ ALLOCSET_DEFAULT_MINSIZE ,
293+ ALLOCSET_DEFAULT_INITSIZE ,
294+ ALLOCSET_DEFAULT_MAXSIZE );
235295
236296 /*
237- * The strangeness with committing and starting transactions here is
238- * due to wanting to run each table's VACUUM as a separate
239- * transaction, so that we don't hold locks unnecessarily long. Also,
240- * if we are doing VACUUM ANALYZE, the ANALYZE part runs as a separate
241- * transaction from the VACUUM to further reduce locking.
242- *
243297 * vacuum_rel expects to be entered with no transaction active; it will
244298 * start and commit its own transaction. But we are called by an SQL
245299 * command, and so we are executing inside a transaction already. We
246300 * commit the transaction started in PostgresMain() here, and start
247301 * another one before exiting to match the commit waiting for us back
248302 * in PostgresMain().
249- *
250- * In the case of an ANALYZE statement (no vacuum, just analyze) it's
251- * okay to run the whole thing in the outer transaction, and so we
252- * skip transaction start/stop operations.
253303 */
254- if (vacstmt -> vacuum )
304+ if (use_own_xacts )
255305 {
256- if (all_rels )
257- {
258- /*
259- * It's a database-wide VACUUM.
260- *
261- * Compute the initially applicable OldestXmin and FreezeLimit
262- * XIDs, so that we can record these values at the end of the
263- * VACUUM. Note that individual tables may well be processed
264- * with newer values, but we can guarantee that no
265- * (non-shared) relations are processed with older ones.
266- *
267- * It is okay to record non-shared values in pg_database, even
268- * though we may vacuum shared relations with older cutoffs,
269- * because only the minimum of the values present in
270- * pg_database matters. We can be sure that shared relations
271- * have at some time been vacuumed with cutoffs no worse than
272- * the global minimum; for, if there is a backend in some
273- * other DB with xmin = OLDXMIN that's determining the cutoff
274- * with which we vacuum shared relations, it is not possible
275- * for that database to have a cutoff newer than OLDXMIN
276- * recorded in pg_database.
277- */
278- vacuum_set_xid_limits (vacstmt , false,
279- & initialOldestXmin ,
280- & initialFreezeLimit );
281- }
282-
283306 /* matches the StartTransaction in PostgresMain() */
284307 CommitTransactionCommand ();
285308 }
@@ -301,13 +324,13 @@ vacuum(VacuumStmt *vacstmt)
301324 MemoryContext old_context = NULL ;
302325
303326 /*
304- * If we vacuumed, use new transaction for analyze. Otherwise,
327+ * If using separate xacts, start one for analyze. Otherwise,
305328 * we can use the outer transaction, but we still need to call
306329 * analyze_rel in a memory context that will be cleaned up on
307330 * return (else we leak memory while processing multiple
308331 * tables).
309332 */
310- if (vacstmt -> vacuum )
333+ if (use_own_xacts )
311334 {
312335 StartTransactionCommand ();
313336 SetQuerySnapshot (); /* might be needed for functions
@@ -326,7 +349,7 @@ vacuum(VacuumStmt *vacstmt)
326349
327350 StrategyHintVacuum (false);
328351
329- if (vacstmt -> vacuum )
352+ if (use_own_xacts )
330353 CommitTransactionCommand ();
331354 else
332355 {
@@ -339,7 +362,7 @@ vacuum(VacuumStmt *vacstmt)
339362 /*
340363 * Finish up processing.
341364 */
342- if (vacstmt -> vacuum )
365+ if (use_own_xacts )
343366 {
344367 /* here, we are not in a transaction */
345368
@@ -348,7 +371,10 @@ vacuum(VacuumStmt *vacstmt)
348371 * PostgresMain().
349372 */
350373 StartTransactionCommand ();
374+ }
351375
376+ if (vacstmt -> vacuum )
377+ {
352378 /*
353379 * If it was a database-wide VACUUM, print FSM usage statistics
354380 * (we don't make you be superuser to see these).
0 commit comments