WAL replay of truncate fails if the table was dropped - Mailing list pgsql-bugs

From Heikki Linnakangas
Subject WAL replay of truncate fails if the table was dropped
Date
Msg-id 46A0BBAB.5080000@enterprisedb.com
Whole thread Raw
Responses Re: WAL replay of truncate fails if the table was dropped  (Tom Lane <tgl@sss.pgh.pa.us>)
List pgsql-bugs
mdtruncate throws an error if the relation file doesn't exist. However,
that's not an error condition if the relation was dropped later.
Non-existent file should be treated the same as an already truncated
file; we now end up with an unrecoverable database.

This bug seems to be present from 8.0 onwards.

Attached is a test case to reproduce it, along with a patch for CVS
HEAD, and an adapted version of the patch for 8.0-8.2.

Thanks to my colleague Dharmendra Goyal for finding this bug and
constructing an initial test case.

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com
DROP TABLE test;
CREATE TABLE test(i char(1800));

BEGIN;
INSERT INTO test VALUES('a');
ROLLBACK;
-- Crash database. This leaves behind an unitialized page at the end of the
-- relation, which vacuum will then truncate.
\! DATA=`psql regression -t -c 'show data_directory'`&& echo $DATA; pg_ctl restart  -w -m immediate  -D $DATA
\c regression;

VACUUM test;

DROP TABLE test;
-- Crash database
\! DATA=`psql regression -t -c 'show data_directory'`&& echo $DATA; pg_ctl restart  -w -m immediate  -D $DATA
\c regression;


Index: src/backend/storage/smgr/md.c
===================================================================
RCS file: /home/hlinnaka/pgcvsrepository/pgsql/src/backend/storage/smgr/md.c,v
retrieving revision 1.128
diff -c -r1.128 md.c
*** src/backend/storage/smgr/md.c    12 Apr 2007 17:10:55 -0000    1.128
--- src/backend/storage/smgr/md.c    20 Jul 2007 12:31:42 -0000
***************
*** 717,722 ****
--- 717,734 ----
  #endif

      /*
+      * Open the relation. We call mdopen before mdnblocks, because that will
+      * fail if the first segment doesn't exist. That can happen in recovery,
+      * if the relation was dropped after the truncate.
+      */
+     v = mdopen(reln, InRecovery ? EXTENSION_RETURN_NULL : EXTENSION_FAIL);
+     if (v == NULL)
+     {
+         Assert(InRecovery);
+         return;
+     }
+
+     /*
       * NOTE: mdnblocks makes sure we have opened all active segments, so
       * that truncation loop will get them all!
       */
***************
*** 736,743 ****
      if (nblocks == curnblk)
          return;                    /* no work */

-     v = mdopen(reln, EXTENSION_FAIL);
-
  #ifndef LET_OS_MANAGE_FILESIZE
      priorblocks = 0;
      while (v != NULL)
--- 748,753 ----
Index: src/backend/storage/smgr/md.c
===================================================================
RCS file: /home/hlinnaka/pgcvsrepository/pgsql/src/backend/storage/smgr/md.c,v
retrieving revision 1.114.4.2
diff -c -r1.114.4.2 md.c
*** src/backend/storage/smgr/md.c    26 Apr 2007 23:25:30 -0000    1.114.4.2
--- src/backend/storage/smgr/md.c    20 Jul 2007 12:37:45 -0000
***************
*** 601,606 ****
--- 601,618 ----
  #endif

      /*
+      * Open the relation. We call mdopen before mdnblocks, because that will
+      * fail if the first segment doesn't exist. That can happen in recovery,
+      * if the relation was dropped after the truncate.
+      */
+     v = mdopen(reln, InRecovery);
+     if (v == NULL)
+     {
+         Assert(InRecovery);
+         return;
+     }
+
+     /*
       * NOTE: mdnblocks makes sure we have opened all active segments, so
       * that truncation loop will get them all!
       */
***************
*** 612,619 ****
      if (nblocks == curnblk)
          return nblocks;            /* no work */

-     v = mdopen(reln, false);
-
  #ifndef LET_OS_MANAGE_FILESIZE
      priorblocks = 0;
      while (v != NULL)
--- 624,629 ----

pgsql-bugs by date:

Previous
From: Magnus Hagander
Date:
Subject: Re: BUG #3475: SQL: 7/2 = 3
Next
From: Tom Lane
Date:
Subject: Re: WAL replay of truncate fails if the table was dropped