From f3e5d8620c7c2fa7ca5a1a8cc969c08856756c9d Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 10 Jul 2000 04:32:00 +0000 Subject: [PATCH] Prevent creating a boatload of empty segments when md.c is asked to access a ridiculously large block number within a relation. --- src/backend/storage/smgr/md.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c index de51e703f8..5baf6935d0 100644 --- a/src/backend/storage/smgr/md.c +++ b/src/backend/storage/smgr/md.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.72 2000/06/28 03:32:14 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.73 2000/07/10 04:32:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -714,9 +714,16 @@ mdnblocks(Relation reln) if (v->mdfd_chain == (MdfdVec *) NULL) { + /* + * Because we pass O_CREAT, we will create the next segment + * (with zero length) immediately, if the last segment is of + * length REL_SEGSIZE. This is unnecessary but harmless, and + * testing for the case would take more cycles than it seems + * worth. + */ v->mdfd_chain = _mdfd_openseg(reln, segno, O_CREAT); if (v->mdfd_chain == (MdfdVec *) NULL) - elog(ERROR, "cannot count blocks for %s -- open failed", + elog(ERROR, "cannot count blocks for %s -- open failed: %m", RelationGetRelationName(reln)); } @@ -1038,11 +1045,20 @@ _mdfd_getseg(Relation reln, int blkno) if (v->mdfd_chain == (MdfdVec *) NULL) { - v->mdfd_chain = _mdfd_openseg(reln, i, O_CREAT); + /* + * We will create the next segment only if the target block + * is within it. This prevents Sorcerer's Apprentice syndrome + * if a bug at higher levels causes us to be handed a ridiculously + * large blkno --- otherwise we could create many thousands of + * empty segment files before reaching the "target" block. We + * should never need to create more than one new segment per call, + * so this restriction seems reasonable. + */ + v->mdfd_chain = _mdfd_openseg(reln, i, (segno == 1) ? O_CREAT : 0); if (v->mdfd_chain == (MdfdVec *) NULL) - elog(ERROR, "cannot open segment %d of relation %s", - i, RelationGetRelationName(reln)); + elog(ERROR, "cannot open segment %d of relation %s (target block %d): %m", + i, RelationGetRelationName(reln), blkno); } v = v->mdfd_chain; } @@ -1060,8 +1076,10 @@ _mdfd_getseg(Relation reln, int blkno) * "blind" with no Relation struct. We assume that we are not likely to * touch the same relation again soon, so we do not create an FD entry for * the relation --- we just open a kernel file descriptor which will be - * used and promptly closed. The return value is the kernel descriptor, - * or -1 on failure. + * used and promptly closed. We also assume that the target block already + * exists, ie, we need not extend the relation. + * + * The return value is the kernel descriptor, or -1 on failure. */ static int