diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c index 3a74dbf0ed92..7888efc153be 100644 --- a/src/backend/storage/smgr/md.c +++ b/src/backend/storage/smgr/md.c @@ -1282,6 +1282,9 @@ mdnblocks(SMgrRelation reln, ForkNumber forknum) * functions for this relation or handled interrupts in between. This makes * sure we have opened all active segments, so that truncate loop will get * them all! + * + * If nblocks > curnblk, the request is ignored when we are in InRecovery, + * otherwise, an error is raised. */ void mdtruncate(SMgrRelation reln, ForkNumber forknum, diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c index 8943528fe1c9..2e45f17cd0d6 100644 --- a/src/backend/storage/smgr/smgr.c +++ b/src/backend/storage/smgr/smgr.c @@ -870,6 +870,9 @@ smgrnblocks_cached(SMgrRelation reln, ForkNumber forknum) * be called in a critical section, but the current size must be checked * outside the critical section, and no interrupts or smgr functions relating * to this relation should be called in between. + * + * If the specified number of blocks is higher than the current size, the + * request is ignored when we are InRecovery, otherwise, an error is raised. */ void smgrtruncate(SMgrRelation reln, ForkNumber *forknum, int nforks, @@ -910,8 +913,15 @@ smgrtruncate(SMgrRelation reln, ForkNumber *forknum, int nforks, * backends to invalidate their copies of smgr_cached_nblocks, and * these ones too at the next command boundary. But ensure they aren't * outright wrong until then. + * + * nblocks > oldblocks can happen when a relation is truncated + * multiple times and the restartpoint is located before the + * truncates. The relation on disk will have the size of the second + * truncate and when replaying the first truncate, we will have + * nblocks > curnblk. We must restore old_nblocks when this happens. */ - reln->smgr_cached_nblocks[forknum[i]] = nblocks[i]; + reln->smgr_cached_nblocks[forknum[i]] = + nblocks[i] > old_nblocks[i] ? old_nblocks[i] : nblocks[i]; } }