How embarrasing...

I hope it's all there now.

On Thu, 28 Apr 2005, Bruce Momjian wrote:

Uh, you forgot to add cleanup.h.

---------------------------------------------------------------------------

Heikki Linnakangas wrote:
On Tue, 26 Apr 2005, Alvaro Herrera wrote:

You forgot the attachment?

Damn. It happens time after time...

- Heikki

Content-Description:

[ Attachment, skipping... ]

--
 Bruce Momjian                        |  http://candle.pha.pa.us
 pgman@candle.pha.pa.us               |  (610) 359-1001
 +  If your life is a hard drive,     |  13 Roberts Road
 +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073


- Heikki
Index: doc/src/sgml/maintenance.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/maintenance.sgml,v
retrieving revision 1.41
diff -c -r1.41 maintenance.sgml
*** doc/src/sgml/maintenance.sgml       20 Feb 2005 02:21:26 -0000      1.41
--- doc/src/sgml/maintenance.sgml       28 Apr 2005 17:32:41 -0000
***************
*** 474,479 ****
--- 474,496 ----
    </para>
   </sect1>
  
+  <sect1 id="cleanup-after-crash">
+   <title>Cleanup after crash</title>
+ 
+   <indexterm zone="cleanup-after-crash">
+    <primary>stale file</primary>
+   </indexterm>
+ 
+   <para>
+    <productname>PostgreSQL</productname> recovers automatically after crash
+    using the write-ahead log (see <xref linkend="wal">) and no manual 
+    operations are normally needed. However, if there was a transaction 
running 
+    when the crash occured that created or dropped a relation, the 
+    transaction might have left a stale file in the data directory. If this 
+    happens, you will get a notice in the log file stating which files can be 
+    deleted.
+   </para>
+  </sect1>
  
   <sect1 id="logfile-maintenance">
    <title>Log File Maintenance</title>
Index: src/backend/access/transam/xlog.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/access/transam/xlog.c,v
retrieving revision 1.188
diff -c -r1.188 xlog.c
*** src/backend/access/transam/xlog.c   23 Apr 2005 18:49:54 -0000      1.188
--- src/backend/access/transam/xlog.c   28 Apr 2005 17:32:41 -0000
***************
*** 42,47 ****
--- 42,48 ----
  #include "utils/builtins.h"
  #include "utils/guc.h"
  #include "utils/relcache.h"
+ #include "utils/cleanup.h"
  
  
  /*
***************
*** 4520,4525 ****
--- 4521,4528 ----
  
                CreateCheckPoint(true, true);
  
+               CleanupStaleRelFiles();
+ 
                /*
                 * Close down recovery environment
                 */
Index: src/backend/catalog/catalog.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/catalog/catalog.c,v
retrieving revision 1.59
diff -c -r1.59 catalog.c
*** src/backend/catalog/catalog.c       14 Apr 2005 20:03:23 -0000      1.59
--- src/backend/catalog/catalog.c       28 Apr 2005 17:32:41 -0000
***************
*** 106,111 ****
--- 106,144 ----
        return path;
  }
  
+ /*
+  * GetTablespacePath  - construct path to a tablespace symbolic link
+  *
+  * Result is a palloc'd string.
+  *
+  * XXX this must agree with relpath and GetDatabasePath!
+  */
+ char *
+ GetTablespacePath(Oid spcNode)
+ {
+       int                     pathlen;
+       char       *path;
+ 
+       Assert(spcNode != GLOBALTABLESPACE_OID);
+ 
+       if (spcNode == DEFAULTTABLESPACE_OID)
+       {
+               /* The default tablespace is {datadir}/base */
+               pathlen = strlen(DataDir) + 5 + 1;
+               path = (char *) palloc(pathlen);
+               snprintf(path, pathlen, "%s/base",
+                                DataDir);
+       }
+       else
+       {
+               /* All other tablespaces have symlinks in pg_tblspc */
+               pathlen = strlen(DataDir) + 11 + OIDCHARS + 1;
+               path = (char *) palloc(pathlen);
+               snprintf(path, pathlen, "%s/pg_tblspc/%u",
+                                DataDir, spcNode);
+       }
+       return path;
+ }
  
  /*
   * IsSystemRelation
Index: src/backend/commands/tablespace.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/commands/tablespace.c,v
retrieving revision 1.17
diff -c -r1.17 tablespace.c
*** src/backend/commands/tablespace.c   14 Apr 2005 20:03:24 -0000      1.17
--- src/backend/commands/tablespace.c   28 Apr 2005 17:32:41 -0000
***************
*** 341,348 ****
        /*
         * All seems well, create the symlink
         */
!       linkloc = (char *) palloc(strlen(DataDir) + 11 + 10 + 1);
!       sprintf(linkloc, "%s/pg_tblspc/%u", DataDir, tablespaceoid);
  
        if (symlink(location, linkloc) < 0)
                ereport(ERROR,
--- 341,347 ----
        /*
         * All seems well, create the symlink
         */
!       linkloc = GetTablespacePath(tablespaceoid);
  
        if (symlink(location, linkloc) < 0)
                ereport(ERROR,
***************
*** 495,502 ****
        char       *subfile;
        struct stat st;
  
!       location = (char *) palloc(strlen(DataDir) + 11 + 10 + 1);
!       sprintf(location, "%s/pg_tblspc/%u", DataDir, tablespaceoid);
  
        /*
         * Check if the tablespace still contains any files.  We try to rmdir
--- 494,500 ----
        char       *subfile;
        struct stat st;
  
!       location = GetTablespacePath(tablespaceoid);
  
        /*
         * Check if the tablespace still contains any files.  We try to rmdir
***************
*** 1036,1043 ****
                set_short_version(location);
  
                /* Create the symlink if not already present */
!               linkloc = (char *) palloc(strlen(DataDir) + 11 + 10 + 1);
!               sprintf(linkloc, "%s/pg_tblspc/%u", DataDir, xlrec->ts_id);
  
                if (symlink(location, linkloc) < 0)
                {
--- 1034,1040 ----
                set_short_version(location);
  
                /* Create the symlink if not already present */
!               linkloc = GetTablespacePath(xlrec->ts_id);
  
                if (symlink(location, linkloc) < 0)
                {
Index: src/backend/utils/adt/misc.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/adt/misc.c,v
retrieving revision 1.40
diff -c -r1.40 misc.c
*** src/backend/utils/adt/misc.c        31 Dec 2004 22:01:22 -0000      1.40
--- src/backend/utils/adt/misc.c        28 Apr 2005 17:32:42 -0000
***************
*** 26,31 ****
--- 26,32 ----
  #include "funcapi.h"
  #include "catalog/pg_type.h"
  #include "catalog/pg_tablespace.h"
+ #include "catalog/catalog.h"
  
  #define atooid(x)  ((Oid) strtoul((x), NULL, 10))
  
***************
*** 144,154 ****
  
                fctx = palloc(sizeof(ts_db_fctx));
  
-               /*
-                * size = path length + tablespace dirname length + 2 dir sep
-                * chars + oid + terminator
-                */
-               fctx->location = (char *) palloc(strlen(DataDir) + 11 + 10 + 1);
                if (tablespaceOid == GLOBALTABLESPACE_OID)
                {
                        fctx->dirdesc = NULL;
--- 145,150 ----
***************
*** 157,168 ****
                }
                else
                {
!                       if (tablespaceOid == DEFAULTTABLESPACE_OID)
!                               sprintf(fctx->location, "%s/base", DataDir);
!                       else
!                               sprintf(fctx->location, "%s/pg_tblspc/%u", 
DataDir,
!                                               tablespaceOid);
! 
                        fctx->dirdesc = AllocateDir(fctx->location);
  
                        if (!fctx->dirdesc)
--- 153,159 ----
                }
                else
                {
!                       fctx->location = GetTablespacePath(tablespaceOid);
                        fctx->dirdesc = AllocateDir(fctx->location);
  
                        if (!fctx->dirdesc)
Index: src/backend/utils/init/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/init/Makefile,v
retrieving revision 1.18
diff -c -r1.18 Makefile
*** src/backend/utils/init/Makefile     20 Feb 2005 02:22:00 -0000      1.18
--- src/backend/utils/init/Makefile     28 Apr 2005 17:32:42 -0000
***************
*** 12,18 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = flatfiles.o globals.o miscinit.o postinit.o
  
  all: SUBSYS.o
  
--- 12,18 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = flatfiles.o globals.o miscinit.o postinit.o cleanup.o
  
  all: SUBSYS.o
  
Index: src/backend/utils/init/cleanup.c
===================================================================
RCS file: src/backend/utils/init/cleanup.c
diff -N src/backend/utils/init/cleanup.c
*** /dev/null   1 Jan 1970 00:00:00 -0000
--- src/backend/utils/init/cleanup.c    28 Apr 2005 17:32:42 -0000
***************
*** 0 ****
--- 1,209 ----
+ /*-------------------------------------------------------------------------
+  *
+  * cleanup.c
+  *      support to clean up stale relation files on crash recovery
+  *
+  * If a backend crashes while in a transaction that has created or
+  * deleted a relfilenode, a stale file can be left over in the data
+  * directory. This file contains routines to clean up those stale
+  * files on recovery.
+  *
+  * $PostgreSQL$
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "storage/fd.h"
+ 
+ #include "utils/cleanup.h"
+ #include "miscadmin.h"
+ #include "catalog/pg_tablespace.h"
+ #include "catalog/catalog.h"
+ #include "access/skey.h"
+ #include "utils/fmgroids.h"
+ #include "access/relscan.h"
+ #include "access/heapam.h"
+ #include "utils/resowner.h"
+ 
+ static void CleanupStaleRelFilesFrom(Oid tablespaceoid, Oid dboid);
+ static void CleanupStaleRelFilesFromTablespace(Oid tablespaceoid);
+ 
+ /* Like AllocateDir, but ereports on failure */
+ static DIR *
+ AllocateDirChecked(char *path)
+ {
+       DIR *dirdesc = AllocateDir(path);
+       if (dirdesc == NULL)
+               ereport(ERROR,
+                               (errcode_for_file_access(),
+                                errmsg("could not open directory \"%s\": %m",
+                                               path)));
+       return dirdesc;
+ }
+ 
+ /*
+  * Scan through all tablespaces for relations left over
+  * by aborted transactions.
+  *
+  * For example, if a transaction issues
+  * BEGIN; CREATE TABLE foobar ();
+  * and then the backend crashes, the file is left in the
+  * tablespace until CleanupStaleRelFiles deletes it.
+  */
+ void
+ CleanupStaleRelFiles(void)
+ {
+       DIR                *dirdesc;
+       struct dirent *de;
+       char       *path;
+       int pathlen;
+ 
+       pathlen = strlen(DataDir) + 11 + 1;
+       path = (char *) palloc(pathlen);
+       snprintf(path, pathlen, "%s/pg_tblspc/", DataDir);
+       dirdesc = AllocateDirChecked(path);
+       while ((de = readdir(dirdesc)) != NULL)
+       {
+               char *invalid;
+               Oid tablespaceoid;
+ 
+               /* Check that the directory name looks like valid tablespace 
link.      */
+               tablespaceoid = (Oid) strtol(de->d_name, &invalid, 10);
+               if(invalid[0] == '\0')
+                       CleanupStaleRelFilesFromTablespace(tablespaceoid);
+       }
+       pfree(path);
+ 
+       CleanupStaleRelFilesFromTablespace(DEFAULTTABLESPACE_OID);
+ }
+ 
+ /* Scan a specific tablespace for stale relations */
+ static void
+ CleanupStaleRelFilesFromTablespace(Oid tablespaceoid)
+ {
+       DIR                *dirdesc;
+       struct dirent *de;
+       char       *path;
+ 
+       path = GetTablespacePath(tablespaceoid);
+ 
+       dirdesc = AllocateDirChecked(path);
+       while ((de = readdir(dirdesc)) != NULL)
+       {
+               char *invalid;
+               Oid dboid;
+ 
+               dboid = (Oid) strtol(de->d_name, &invalid, 10);
+               if(invalid[0] == '\0')
+                       CleanupStaleRelFilesFrom(tablespaceoid, dboid);
+       }
+       pfree(path);
+ }
+ 
+ /* Scan a specific database in a specific tablespace for stale relations.
+  *
+  * First, pg_class for the database is opened, and the relfilenodes of all
+  * relations mentioned there are stored in a hash table.
+  *
+  * Then the directory is scanned. Every file in the directory that's not
+  * found in pg_class (the hash table) is logged.
+  */
+ static void
+ CleanupStaleRelFilesFrom(Oid tablespaceoid, Oid dboid)
+ {
+       DIR *dirdesc;
+       struct dirent *de;
+       HASHCTL hashctl;
+       HTAB *relfilenodeHash;
+       MemoryContext mcxt;
+       RelFileNode rnode;
+       char *path;
+ 
+       /* We create a private memory context so that we can easily deallocate
+        * the hash table and its contents
+        */
+       mcxt = AllocSetContextCreate(TopMemoryContext, "CleanupStaleRelFiles",
+                                                                
ALLOCSET_DEFAULT_MINSIZE,
+                                                                
ALLOCSET_DEFAULT_INITSIZE,
+                                                                
ALLOCSET_DEFAULT_MAXSIZE);
+ 
+       hashctl.hash = tag_hash;
+       /* The entry contents is not used for anything, we just check
+        * if an oid is in the hash table or not. */
+       hashctl.keysize = sizeof(Oid);
+       hashctl.entrysize = 1;
+       hashctl.hcxt = mcxt;
+       relfilenodeHash = hash_create("relfilenodeHash", 100, &hashctl,
+                                                                 HASH_FUNCTION
+                                                                 | HASH_ELEM | 
HASH_CONTEXT);
+ 
+       /* Read all relfilenodes from pg_class into the hash table */
+       {
+               ResourceOwner owner;
+               ResourceOwner oldowner;
+               Relation        rel;
+               HeapScanDesc scan;
+               HeapTuple tuple;
+ 
+               /* Need a resowner to keep the heapam and buffer code happy */
+               owner = ResourceOwnerCreate(NULL, "CleanupStaleRelFiles");
+               oldowner = CurrentResourceOwner;
+               CurrentResourceOwner = owner;
+ 
+               rnode.spcNode = tablespaceoid;
+               rnode.dbNode = dboid;
+               rnode.relNode = RelationRelationId;
+               rel = XLogOpenRelation(true, 0 , rnode);
+ 
+               scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+               while ((tuple = heap_getnext(scan, ForwardScanDirection)) != 
NULL)
+               {
+                       Form_pg_class classform = (Form_pg_class) 
GETSTRUCT(tuple);
+ 
+                       hash_search(relfilenodeHash, &classform->relfilenode,
+                                               HASH_ENTER, NULL);
+               }
+               heap_endscan(scan);
+ 
+               XLogCloseRelation(rnode);
+               CurrentResourceOwner = oldowner;
+               ResourceOwnerDelete(owner);
+       }
+ 
+       /* Scan the directory */
+       path = GetDatabasePath(dboid, tablespaceoid);
+ 
+       dirdesc = AllocateDirChecked(path);
+       while ((de = readdir(dirdesc)) != NULL)
+       {
+               char *invalid;
+               Oid relfilenode;
+ 
+               relfilenode = strtol(de->d_name, &invalid, 10);
+               if(invalid[0] == '\0')
+               {
+                       /* Filename was a valid number, check if pg_class knows
+                          about it */
+                       if(hash_search(relfilenodeHash, &relfilenode,
+                                                  HASH_FIND, NULL) == NULL)
+                       {
+                               char *filepath;
+                               rnode.spcNode = tablespaceoid;
+                               rnode.dbNode = dboid;
+                               rnode.relNode = relfilenode;
+                               
+                               filepath = relpath(rnode);
+ 
+                               ereport(NOTICE,
+                                               (errcode_for_file_access(),
+                                                errmsg("The relation file 
\"%s\" is stale and can be safely deleted",
+                                                               filepath)));
+                               pfree(filepath);
+                       }
+               }
+       }
+       pfree(path);
+       hash_destroy(relfilenodeHash);
+       MemoryContextDelete(mcxt);
+ }
Index: src/include/catalog/catalog.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/catalog/catalog.h,v
retrieving revision 1.30
diff -c -r1.30 catalog.h
*** src/include/catalog/catalog.h       31 Dec 2004 22:03:24 -0000      1.30
--- src/include/catalog/catalog.h       28 Apr 2005 17:32:43 -0000
***************
*** 19,24 ****
--- 19,25 ----
  
  extern char *relpath(RelFileNode rnode);
  extern char *GetDatabasePath(Oid dbNode, Oid spcNode);
+ extern char *GetTablespacePath(Oid spcNode);
  
  extern bool IsSystemRelation(Relation relation);
  extern bool IsToastRelation(Relation relation);
Index: src/include/utils/cleanup.h
===================================================================
RCS file: src/include/utils/cleanup.h
diff -N src/include/utils/cleanup.h
*** /dev/null   1 Jan 1970 00:00:00 -0000
--- src/include/utils/cleanup.h 28 Apr 2005 17:32:44 -0000
***************
*** 0 ****
--- 1,15 ----
+ /*-------------------------------------------------------------------------
+  *
+  * cleanup.h
+  *      Routines for fsck-style cleanup on crash recovery
+  *
+  * $PostgreSQL$
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef CLEANUP_H
+ #define CLEANUP_H
+ 
+ extern void CleanupStaleRelFiles(void);
+ 
+ #endif   /* CLEANUP_H */
---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
    (send "unregister YourEmailAddressHere" to [EMAIL PROTECTED])

Reply via email to