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 ----
---------------------------(end of broadcast)--------------------------- TIP 9: In versions below 8.0, the planner will ignore your desire to choose an index scan if your joining column's datatypes do not match