/*
  * Each backend has a hashtable that stores all extant SMgrRelation objects.
+ * In addition, "unowned" SMgrRelation objects are chained together in a list.
  */
 static HTAB *SMgrRelationHash = NULL;
 
+static SMgrRelation first_unowned_reln = NULL;
+
 /* local function prototypes */
 static void smgrshutdown(int code, Datum arg);
+static void remove_from_unowned_list(SMgrRelation reln);
 
 
 /*
 /*
  * smgropen() -- Return an SMgrRelation object, creating it if need be.
  *
- *     This does not attempt to actually open the object.
+ *     This does not attempt to actually open the underlying file.
  */
 SMgrRelation
 smgropen(RelFileNode rnode, BackendId backend)
        ctl.hash = tag_hash;
        SMgrRelationHash = hash_create("smgr relation table", 400,
                                       &ctl, HASH_ELEM | HASH_FUNCTION);
+       first_unowned_reln = NULL;
    }
 
    /* Look up or create an entry */
        /* mark it not open */
        for (forknum = 0; forknum <= MAX_FORKNUM; forknum++)
            reln->md_fd[forknum] = NULL;
+
+       /* place it at head of unowned list (to make smgrsetowner cheap) */
+       reln->next_unowned_reln = first_unowned_reln;
+       first_unowned_reln = reln;
    }
 
    return reln;
 void
 smgrsetowner(SMgrRelation *owner, SMgrRelation reln)
 {
+   /* We don't currently support "disowning" an SMgrRelation here */
+   Assert(owner != NULL);
+
    /*
     * First, unhook any old owner.  (Normally there shouldn't be any, but it
     * seems possible that this can happen during swap_relation_files()
     * depending on the order of processing.  It's ok to close the old
     * relcache entry early in that case.)
+    *
+    * If there isn't an old owner, then the reln should be in the unowned
+    * list, and we need to remove it.
     */
    if (reln->smgr_owner)
        *(reln->smgr_owner) = NULL;
+   else
+       remove_from_unowned_list(reln);
 
    /* Now establish the ownership relationship. */
    reln->smgr_owner = owner;
    *owner = reln;
 }
 
+/*
+ * remove_from_unowned_list -- unlink an SMgrRelation from the unowned list
+ *
+ * If the reln is not present in the list, nothing happens.  Typically this
+ * would be caller error, but there seems no reason to throw an error.
+ *
+ * In the worst case this could be rather slow; but in all the cases that seem
+ * likely to be performance-critical, the reln being sought will actually be
+ * first in the list.  Furthermore, the number of unowned relns touched in any
+ * one transaction shouldn't be all that high typically.  So it doesn't seem
+ * worth expending the additional space and management logic needed for a
+ * doubly-linked list.
+ */
+static void
+remove_from_unowned_list(SMgrRelation reln)
+{
+   SMgrRelation *link;
+   SMgrRelation cur;
+
+   for (link = &first_unowned_reln, cur = *link;
+        cur != NULL;
+        link = &cur->next_unowned_reln, cur = *link)
+   {
+       if (cur == reln)
+       {
+           *link = cur->next_unowned_reln;
+           cur->next_unowned_reln = NULL;
+           break;
+       }
+   }
+}
+
 /*
  * smgrexists() -- Does the underlying file for a fork exist?
  */
 
    owner = reln->smgr_owner;
 
+   if (!owner)
+       remove_from_unowned_list(reln);
+
    if (hash_search(SMgrRelationHash,
                    (void *) &(reln->smgr_rnode),
                    HASH_REMOVE, NULL) == NULL)
            (*(smgrsw[i].smgr_post_ckpt)) ();
    }
 }
+
+/*
+ * AtEOXact_SMgr
+ *
+ * This routine is called during transaction commit or abort (it doesn't
+ * particularly care which).  All transient SMgrRelation objects are closed.
+ *
+ * We do this as a compromise between wanting transient SMgrRelations to
+ * live awhile (to amortize the costs of blind writes of multiple blocks)
+ * and needing them to not live forever (since we're probably holding open
+ * a kernel file descriptor for the underlying file, and we need to ensure
+ * that gets closed reasonably soon if the file gets deleted).
+ */
+void
+AtEOXact_SMgr(void)
+{
+   /*
+    * Zap all unowned SMgrRelations.  We rely on smgrclose() to remove each
+    * one from the list.
+    */
+   while (first_unowned_reln != NULL)
+   {
+       Assert(first_unowned_reln->smgr_owner == NULL);
+       smgrclose(first_unowned_reln);
+   }
+}
 
  * without having to make the smgr explicitly aware of relcache.  There
  * can't be more than one "owner" pointer per SMgrRelation, but that's
  * all we need.
+ *
+ * SMgrRelations that do not have an "owner" are considered to be transient,
+ * and are deleted at end of transaction.
  */
 typedef struct SMgrRelationData
 {
 
    /* for md.c; NULL for forks that are not open */
    struct _MdfdVec *md_fd[MAX_FORKNUM + 1];
+
+   /* if unowned, list link in list of all unowned SMgrRelations */
+   struct SMgrRelationData *next_unowned_reln;
 } SMgrRelationData;
 
 typedef SMgrRelationData *SMgrRelation;
 extern void smgrpreckpt(void);
 extern void smgrsync(void);
 extern void smgrpostckpt(void);
+extern void AtEOXact_SMgr(void);
 
 
 /* internals: move me elsewhere -- ay 7/94 */