diff --git a/src/backend/storage/file/buffile.c b/src/backend/storage/file/buffile.c
index c4afe4d368a..1d1efcd139b 100644
--- a/src/backend/storage/file/buffile.c
+++ b/src/backend/storage/file/buffile.c
@@ -419,8 +419,10 @@ BufFileClose(BufFile *file)
 	/* close and delete the underlying file(s) */
 	for (i = 0; i < file->numFiles; i++)
 		FileClose(file->files[i]);
-	/* release the buffer space */
+	/* release the buffer space and other metadata */
 	pfree(file->files);
+	if (file->name)
+		pfree((void *) file->name);
 	pfree(file);
 }
 
@@ -883,10 +885,10 @@ BufFileSize(BufFile *file)
 /*
  * Append the contents of the source file to the end of the target file.
  *
- * Note that operation subsumes ownership of underlying resources from
- * "source".  Caller should never call BufFileClose against source having
- * called here first.  Resource owners for source and target must match,
- * too.
+ * Note that this operation subsumes ownership of underlying resources from
+ * "source", and frees the source wrapper and metadata before returning.
+ * Callers must not reference source after this call.  Resource owners for
+ * source and target must match, too.
  *
  * This operation works by manipulating lists of segment files, so the
  * file content is always appended at a MAX_PHYSICAL_FILESIZE-aligned
@@ -907,6 +909,7 @@ BufFileAppend(BufFile *target, BufFile *source)
 
 	Assert(source->readOnly);
 	Assert(!source->dirty);
+	Assert(target != source);
 
 	if (target->resowner != source->resowner)
 		elog(ERROR, "could not append BufFile with non-matching resource owner");
@@ -917,6 +920,15 @@ BufFileAppend(BufFile *target, BufFile *source)
 		target->files[i] = source->files[i - target->numFiles];
 	target->numFiles = newNumFiles;
 
+	/*
+	 * The underlying files now belong to target.  Free only source's wrapper
+	 * and metadata, leaving the transferred file handles open.
+	 */
+	if (source->name)
+		pfree((void *) source->name);
+	pfree(source->files);
+	pfree(source);
+
 	return startBlock;
 }
 
