77 *
88 *
99 * IDENTIFICATION
10- * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.126 1999/11/25 00:15:57 momjian Exp $
10+ * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.127 1999/11/28 02:10:01 tgl Exp $
1111 *
1212 *-------------------------------------------------------------------------
1313 */
2121
2222#include "access/genam.h"
2323#include "access/heapam.h"
24+ #include "access/xact.h"
2425#include "catalog/catalog.h"
2526#include "catalog/catname.h"
2627#include "catalog/index.h"
3334#include "parser/parse_oper.h"
3435#include "storage/sinval.h"
3536#include "storage/smgr.h"
37+ #include "tcop/tcopprot.h"
3638#include "utils/builtins.h"
3739#include "utils/inval.h"
3840#include "utils/portal.h"
4648#include <sys/resource.h>
4749#endif
4850
49- /* #include <port-protos.h> */ /* Why? */
5051
5152bool VacuumRunning = false;
5253
@@ -80,11 +81,10 @@ static void vc_scanoneind(Relation indrel, int num_tuples);
8081static void vc_attrstats (Relation onerel ,VRelStats * vacrelstats ,HeapTuple tuple );
8182static void vc_bucketcpy (Form_pg_attribute attr ,Datum value ,Datum * bucket ,int * bucket_len );
8283static void vc_updstats (Oid relid ,int num_pages ,int num_tuples ,bool hasindex ,VRelStats * vacrelstats );
83- static void vc_delhilowstats (Oid relid ,int attcnt ,int * attnums );
84+ static void vc_delstats (Oid relid ,int attcnt ,int * attnums );
8485static VPageDescr vc_tidreapped (ItemPointer itemptr ,VPageList vpl );
8586static void vc_reappage (VPageList vpl ,VPageDescr vpc );
8687static void vc_vpinsert (VPageList vpl ,VPageDescr vpnew );
87- static void vc_free (VRelList vrl );
8888static void vc_getindices (Oid relid ,int * nindices ,Relation * * Irel );
8989static void vc_clsindices (int nindices ,Relation * Irel );
9090static void vc_mkindesc (Relation onerel ,int nindices ,Relation * Irel ,IndDesc * * Idesc );
@@ -98,76 +98,73 @@ static bool vc_enough_space(VPageDescr vpd, Size len);
9898void
9999vacuum (char * vacrel ,bool verbose ,bool analyze ,List * va_spec )
100100{
101- char * pname ;
102- MemoryContext old ;
103- PortalVariableMemory pmem ;
104101NameData VacRel ;
102+ PortalVariableMemory pmem ;
103+ MemoryContext old ;
105104List * le ;
106105List * va_cols = NIL ;
107106
107+ if (va_spec != NIL && !analyze )
108+ elog (ERROR ,"Can't vacuum columns, only tables. You can 'vacuum analyze' columns." );
109+
108110/*
109- * Create a portal for safe memory across transctions.We need to
110- * palloc the name space for it because our hash function expects the
111- * name to be on a longword boundary. CreatePortal copies the name to
112- * safe storage for us.
111+ * We cannot run VACUUM inside a user transaction block; if we were
112+ * inside a transaction, then our commit- and start-transaction-command
113+ * calls would not have the intended effect! Furthermore, the forced
114+ * commit that occurs before truncating the relation's file would have
115+ * the effect of committing the rest of the user's transaction too,
116+ * which would certainly not be the desired behavior.
113117 */
114- pname = (char * )palloc (strlen (VACPNAME )+ 1 );
115- strcpy (pname ,VACPNAME );
116- vc_portal = CreatePortal (pname );
117- pfree (pname );
118+ if (IsTransactionBlock ())
119+ elog (ERROR ,"VACUUM cannot run inside a BEGIN/END block" );
120+
121+ /* initialize vacuum cleaner, particularly vc_portal */
122+ vc_init ();
118123
119124if (verbose )
120125MESSAGE_LEVEL = NOTICE ;
121126else
122127MESSAGE_LEVEL = DEBUG ;
123128
124- /* vacrel gets de-allocated on transaction commit */
129+ /* vacrel gets de-allocated on transaction commit, so copy it */
125130if (vacrel )
126131strcpy (NameStr (VacRel ),vacrel );
127132
133+ /* must also copy the column list, if any, to safe storage */
128134pmem = PortalGetVariableMemory (vc_portal );
129135old = MemoryContextSwitchTo ((MemoryContext )pmem );
130-
131- if (va_spec != NIL && !analyze )
132- elog (ERROR ,"Can't vacuum columns, only tables. You can 'vacuum analyze' columns." );
133-
134136foreach (le ,va_spec )
135137{
136138char * col = (char * )lfirst (le );
137- char * dest ;
138139
139- dest = (char * )palloc (strlen (col )+ 1 );
140- strcpy (dest ,col );
141- va_cols = lappend (va_cols ,dest );
140+ va_cols = lappend (va_cols ,pstrdup (col ));
142141}
143142MemoryContextSwitchTo (old );
144143
145- /* initialize vacuum cleaner */
146- vc_init ();
147-
148144/* vacuum the database */
149145if (vacrel )
150146vc_vacuum (& VacRel ,analyze ,va_cols );
151147else
152148vc_vacuum (NULL ,analyze ,NIL );
153149
154- PortalDestroy (& vc_portal );
155-
156150/* clean up */
157151vc_shutdown ();
158152}
159153
160154/*
161155 *vc_init(), vc_shutdown() -- start up and shut down the vacuum cleaner.
162156 *
163- *We run exactly one vacuum cleaner at a time. We use the file system
164- *to guarantee an exclusive lock on vacuuming, since a single vacuum
165- *cleaner instantiation crosses transaction boundaries, and we'd lose
166- *postgres-style locks at the end of every transaction.
157+ *Formerly, there was code here to prevent more than one VACUUM from
158+ *executing concurrently in the same database. However, there's no
159+ *good reason to prevent that, and manually removing lockfiles after
160+ *a vacuum crash was a pain for dbadmins. So, forget about lockfiles,
161+ *and just rely on the exclusive lock we grab on each target table
162+ *to ensure that there aren't two VACUUMs running on the same table
163+ *at the same time.
167164 *
168165 *The strangeness with committing and starting transactions in the
169166 *init and shutdown routines is due to the fact that the vacuum cleaner
170- *is invoked viaa sql command, and so is already executing inside
167+ *is invoked viaan SQL command, and so is already executing inside
171168 *a transaction.We need to leave ourselves in a predictable state
172169 *on entry and exit to the vacuum cleaner. We commit the transaction
173170 *started in PostgresMain() inside vc_init(), and start one in
@@ -177,27 +174,23 @@ vacuum(char *vacrel, bool verbose, bool analyze, List *va_spec)
177174static void
178175vc_init ()
179176{
180- int fd ;
177+ char * pname ;
181178
182- #ifndef __CYGWIN32__
183- if ((fd = open ("pg_vlock" ,O_CREAT |O_EXCL ,0600 ))< 0 )
184- #else
185- if ((fd = open ("pg_vlock" ,O_CREAT |O_EXCL |O_BINARY ,0600 ))< 0 )
186- #endif
187- {
188- elog (ERROR ,"Can't create lock file. Is another vacuum cleaner running?\n\
189- \tIf not, you may remove the pg_vlock file in the %s\n\
190- \tdirectory" ,DatabasePath );
191- }
192- close (fd );
179+ /*
180+ * Create a portal for safe memory across transactions. We need to
181+ * palloc the name space for it because our hash function expects the
182+ * name to be on a longword boundary. CreatePortal copies the name to
183+ * safe storage for us.
184+ */
185+ pname = pstrdup (VACPNAME );
186+ vc_portal = CreatePortal (pname );
187+ pfree (pname );
193188
194189/*
195- * By here, exclusive open on the lock file succeeded.If we abort
196- * for any reason during vacuuming, we need to remove the lock file.
190+ * Set flag to indicate that vc_portal must be removed after an error.
197191 * This global variable is checked in the transaction manager on xact
198192 * abort, and the routine vc_abort() is called if necessary.
199193 */
200-
201194VacuumRunning = true;
202195
203196/* matches the StartTransaction in PostgresMain() */
@@ -221,25 +214,28 @@ vc_shutdown()
221214 */
222215unlink (RELCACHE_INIT_FILENAME );
223216
224- /* remove the vacuum cleaner lock file */
225- if (unlink ("pg_vlock" )< 0 )
226- elog (ERROR ,"vacuum: can't destroy lock file!" );
217+ /*
218+ * Release our portal for cross-transaction memory.
219+ */
220+ PortalDestroy (& vc_portal );
227221
228222/* okay, we're done */
229223VacuumRunning = false;
230224
231225/* matches the CommitTransaction in PostgresMain() */
232226StartTransactionCommand ();
233-
234227}
235228
236229void
237230vc_abort ()
238231{
239- /* on abort, remove the vacuum cleaner lock file */
240- unlink ("pg_vlock" );
241-
232+ /* Clear flag first, to avoid recursion if PortalDestroy elog's */
242233VacuumRunning = false;
234+
235+ /*
236+ * Release our portal for cross-transaction memory.
237+ */
238+ PortalDestroy (& vc_portal );
243239}
244240
245241/*
@@ -259,14 +255,9 @@ vc_vacuum(NameData *VacRelP, bool analyze, List *va_cols)
259255/* get list of relations */
260256vrl = vc_getrels (VacRelP );
261257
262- if (analyze && VacRelP == NULL && vrl != NULL )
263- vc_delhilowstats (InvalidOid ,0 ,NULL );
264-
265258/* vacuum each heap relation */
266259for (cur = vrl ;cur != (VRelList )NULL ;cur = cur -> vrl_next )
267260vc_vacone (cur -> vrl_relid ,analyze ,va_cols );
268-
269- vc_free (vrl );
270261}
271262
272263static VRelList
@@ -381,6 +372,13 @@ vc_vacone(Oid relid, bool analyze, List *va_cols)
381372
382373StartTransactionCommand ();
383374
375+ /*
376+ * Check for user-requested abort. Note we want this to be inside
377+ * a transaction, so xact.c doesn't issue useless NOTICE.
378+ */
379+ if (QueryCancel )
380+ CancelQuery ();
381+
384382/*
385383 * Race condition -- if the pg_class tuple has gone away since the
386384 * last time we saw it, we don't need to vacuum it.
@@ -500,7 +498,8 @@ vc_vacone(Oid relid, bool analyze, List *va_cols)
500498stats -> outfunc = InvalidOid ;
501499}
502500vacrelstats -> va_natts = attr_cnt ;
503- vc_delhilowstats (relid , ((attnums ) ?attr_cnt :0 ),attnums );
501+ /* delete existing pg_statistics rows for relation */
502+ vc_delstats (relid , ((attnums ) ?attr_cnt :0 ),attnums );
504503if (attnums )
505504pfree (attnums );
506505}
@@ -2453,7 +2452,7 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *
24532452CatalogIndexInsert (irelations ,Num_pg_statistic_indices ,sd ,stup );
24542453CatalogCloseIndices (Num_pg_statistic_indices ,irelations );
24552454}
2456-
2455+
24572456/* release allocated space */
24582457pfree (DatumGetPointer (values [Anum_pg_statistic_stacommonval - 1 ]));
24592458pfree (DatumGetPointer (values [Anum_pg_statistic_staloval - 1 ]));
@@ -2478,11 +2477,12 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *
24782477}
24792478
24802479/*
2481- *vc_delhilowstats () -- delete pg_statistics rows
2480+ *vc_delstats () -- delete pg_statistics rows for a relation
24822481 *
2482+ *If a list of attribute numbers is given, only zap stats for those attrs.
24832483 */
24842484static void
2485- vc_delhilowstats (Oid relid ,int attcnt ,int * attnums )
2485+ vc_delstats (Oid relid ,int attcnt ,int * attnums )
24862486{
24872487Relation pgstatistic ;
24882488HeapScanDesc scan ;
@@ -2491,15 +2491,9 @@ vc_delhilowstats(Oid relid, int attcnt, int *attnums)
24912491
24922492pgstatistic = heap_openr (StatisticRelationName ,RowExclusiveLock );
24932493
2494- if (relid != InvalidOid )
2495- {
2496- ScanKeyEntryInitialize (& key ,0x0 ,Anum_pg_statistic_starelid ,
2497- F_OIDEQ ,
2498- ObjectIdGetDatum (relid ));
2499- scan = heap_beginscan (pgstatistic , false,SnapshotNow ,1 ,& key );
2500- }
2501- else
2502- scan = heap_beginscan (pgstatistic , false,SnapshotNow ,0 ,NULL );
2494+ ScanKeyEntryInitialize (& key ,0x0 ,Anum_pg_statistic_starelid ,
2495+ F_OIDEQ ,ObjectIdGetDatum (relid ));
2496+ scan = heap_beginscan (pgstatistic , false,SnapshotNow ,1 ,& key );
25032497
25042498while (HeapTupleIsValid (tuple = heap_getnext (scan ,0 )))
25052499{
@@ -2572,28 +2566,6 @@ vc_vpinsert(VPageList vpl, VPageDescr vpnew)
25722566
25732567}
25742568
2575- static void
2576- vc_free (VRelList vrl )
2577- {
2578- VRelList p_vrl ;
2579- MemoryContext old ;
2580- PortalVariableMemory pmem ;
2581-
2582- pmem = PortalGetVariableMemory (vc_portal );
2583- old = MemoryContextSwitchTo ((MemoryContext )pmem );
2584-
2585- while (vrl != (VRelList )NULL )
2586- {
2587-
2588- /* free rel list entry */
2589- p_vrl = vrl ;
2590- vrl = vrl -> vrl_next ;
2591- pfree (p_vrl );
2592- }
2593-
2594- MemoryContextSwitchTo (old );
2595- }
2596-
25972569static void *
25982570vc_find_eq (void * bot ,int nelem ,int size ,void * elm ,
25992571int (* compar ) (const void * ,const void * ))