Skip to content

Commit f3e5d86

Browse files
committed
Prevent creating a boatload of empty segments when md.c is asked to
access a ridiculously large block number within a relation.
1 parent b54faa1 commit f3e5d86

File tree

1 file changed

+25
-7
lines changed
  • src/backend/storage/smgr

1 file changed

+25
-7
lines changed

src/backend/storage/smgr/md.c

+25-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.72 2000/06/28 03:32:14 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.73 2000/07/10 04:32:00 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -714,9 +714,16 @@ mdnblocks(Relation reln)
714714

715715
if (v->mdfd_chain == (MdfdVec *) NULL)
716716
{
717+
/*
718+
* Because we pass O_CREAT, we will create the next segment
719+
* (with zero length) immediately, if the last segment is of
720+
* length REL_SEGSIZE. This is unnecessary but harmless, and
721+
* testing for the case would take more cycles than it seems
722+
* worth.
723+
*/
717724
v->mdfd_chain = _mdfd_openseg(reln, segno, O_CREAT);
718725
if (v->mdfd_chain == (MdfdVec *) NULL)
719-
elog(ERROR, "cannot count blocks for %s -- open failed",
726+
elog(ERROR, "cannot count blocks for %s -- open failed: %m",
720727
RelationGetRelationName(reln));
721728
}
722729

@@ -1038,11 +1045,20 @@ _mdfd_getseg(Relation reln, int blkno)
10381045

10391046
if (v->mdfd_chain == (MdfdVec *) NULL)
10401047
{
1041-
v->mdfd_chain = _mdfd_openseg(reln, i, O_CREAT);
1048+
/*
1049+
* We will create the next segment only if the target block
1050+
* is within it. This prevents Sorcerer's Apprentice syndrome
1051+
* if a bug at higher levels causes us to be handed a ridiculously
1052+
* large blkno --- otherwise we could create many thousands of
1053+
* empty segment files before reaching the "target" block. We
1054+
* should never need to create more than one new segment per call,
1055+
* so this restriction seems reasonable.
1056+
*/
1057+
v->mdfd_chain = _mdfd_openseg(reln, i, (segno == 1) ? O_CREAT : 0);
10421058

10431059
if (v->mdfd_chain == (MdfdVec *) NULL)
1044-
elog(ERROR, "cannot open segment %d of relation %s",
1045-
i, RelationGetRelationName(reln));
1060+
elog(ERROR, "cannot open segment %d of relation %s (target block %d): %m",
1061+
i, RelationGetRelationName(reln), blkno);
10461062
}
10471063
v = v->mdfd_chain;
10481064
}
@@ -1060,8 +1076,10 @@ _mdfd_getseg(Relation reln, int blkno)
10601076
* "blind" with no Relation struct. We assume that we are not likely to
10611077
* touch the same relation again soon, so we do not create an FD entry for
10621078
* the relation --- we just open a kernel file descriptor which will be
1063-
* used and promptly closed. The return value is the kernel descriptor,
1064-
* or -1 on failure.
1079+
* used and promptly closed. We also assume that the target block already
1080+
* exists, ie, we need not extend the relation.
1081+
*
1082+
* The return value is the kernel descriptor, or -1 on failure.
10651083
*/
10661084

10671085
static int

0 commit comments

Comments
 (0)