Prevent creating a boatload of empty segments when md.c is asked to
authorTom Lane
Mon, 10 Jul 2000 04:32:00 +0000 (04:32 +0000)
committerTom Lane
Mon, 10 Jul 2000 04:32:00 +0000 (04:32 +0000)
access a ridiculously large block number within a relation.

src/backend/storage/smgr/md.c

index de51e703f8b61f63213433b7bd3c79b7de953572..5baf6935d0a0d8e85ffc1897b0d660cd2fece8ea 100644 (file)
@@ -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