t
(1 row)
+RESET role;
+------
+---- Test pg_buffercache_evict* functions
+------
+CREATE ROLE regress_buffercache_normal;
+SET ROLE regress_buffercache_normal;
+-- These should fail because they need to be called as SUPERUSER
+SELECT * FROM pg_buffercache_evict(1);
+ERROR: must be superuser to use pg_buffercache_evict()
+SELECT * FROM pg_buffercache_evict_relation(1);
+ERROR: must be superuser to use pg_buffercache_evict_relation()
+SELECT * FROM pg_buffercache_evict_all();
+ERROR: must be superuser to use pg_buffercache_evict_all()
+RESET ROLE;
+-- These should return nothing, because these are STRICT functions
+SELECT * FROM pg_buffercache_evict(NULL);
+ buffer_evicted | buffer_flushed
+----------------+----------------
+ |
+(1 row)
+
+SELECT * FROM pg_buffercache_evict_relation(NULL);
+ buffers_evicted | buffers_flushed | buffers_skipped
+-----------------+-----------------+-----------------
+ | |
+(1 row)
+
+-- These should fail because they are not called by valid range of buffers
+-- Number of the shared buffers are limited by max integer
+SELECT 2147483647 max_buffers \gset
+SELECT * FROM pg_buffercache_evict(-1);
+ERROR: bad buffer ID: -1
+SELECT * FROM pg_buffercache_evict(0);
+ERROR: bad buffer ID: 0
+SELECT * FROM pg_buffercache_evict(:max_buffers);
+ERROR: bad buffer ID: 2147483647
+-- This should fail because pg_buffercache_evict_relation() doesn't accept
+-- local relations
+CREATE TEMP TABLE temp_pg_buffercache();
+SELECT * FROM pg_buffercache_evict_relation('temp_pg_buffercache');
+ERROR: relation uses local buffers, pg_buffercache_evict_relation() is intended to be used for shared buffers only
+DROP TABLE temp_pg_buffercache;
+-- These shouldn't fail
+SELECT buffer_evicted IS NOT NULL FROM pg_buffercache_evict(1);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT buffers_evicted IS NOT NULL FROM pg_buffercache_evict_all();
+ ?column?
+----------
+ t
+(1 row)
+
+CREATE TABLE shared_pg_buffercache();
+SELECT buffers_evicted IS NOT NULL FROM pg_buffercache_evict_relation('shared_pg_buffercache');
+ ?column?
+----------
+ t
+(1 row)
+
+DROP TABLE shared_pg_buffercache;
+DROP ROLE regress_buffercache_normal;
GRANT EXECUTE ON FUNCTION pg_buffercache_numa_pages() TO pg_monitor;
GRANT SELECT ON pg_buffercache_numa TO pg_monitor;
+
+
+DROP FUNCTION pg_buffercache_evict(integer);
+CREATE FUNCTION pg_buffercache_evict(
+ IN int,
+ OUT buffer_evicted boolean,
+ OUT buffer_flushed boolean)
+AS 'MODULE_PATHNAME', 'pg_buffercache_evict'
+LANGUAGE C PARALLEL SAFE VOLATILE STRICT;
+
+CREATE FUNCTION pg_buffercache_evict_relation(
+ IN regclass,
+ OUT buffers_evicted int4,
+ OUT buffers_flushed int4,
+ OUT buffers_skipped int4)
+AS 'MODULE_PATHNAME', 'pg_buffercache_evict_relation'
+LANGUAGE C PARALLEL SAFE VOLATILE STRICT;
+
+CREATE FUNCTION pg_buffercache_evict_all(
+ OUT buffers_evicted int4,
+ OUT buffers_flushed int4,
+ OUT buffers_skipped int4)
+AS 'MODULE_PATHNAME', 'pg_buffercache_evict_all'
+LANGUAGE C PARALLEL SAFE VOLATILE;
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/relation.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "port/pg_numa.h"
#include "storage/buf_internals.h"
#include "storage/bufmgr.h"
+#include "utils/rel.h"
#define NUM_BUFFERCACHE_PAGES_MIN_ELEM 8
#define NUM_BUFFERCACHE_PAGES_ELEM 9
#define NUM_BUFFERCACHE_SUMMARY_ELEM 5
#define NUM_BUFFERCACHE_USAGE_COUNTS_ELEM 4
+#define NUM_BUFFERCACHE_EVICT_ELEM 2
+#define NUM_BUFFERCACHE_EVICT_RELATION_ELEM 3
+#define NUM_BUFFERCACHE_EVICT_ALL_ELEM 3
#define NUM_BUFFERCACHE_NUMA_ELEM 3
PG_FUNCTION_INFO_V1(pg_buffercache_summary);
PG_FUNCTION_INFO_V1(pg_buffercache_usage_counts);
PG_FUNCTION_INFO_V1(pg_buffercache_evict);
+PG_FUNCTION_INFO_V1(pg_buffercache_evict_relation);
+PG_FUNCTION_INFO_V1(pg_buffercache_evict_all);
/* Only need to touch memory once per backend process lifetime */
return (Datum) 0;
}
+/*
+ * Helper function to check if the user has superuser privileges.
+ */
+static void
+pg_buffercache_superuser_check(char *func_name)
+{
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to use %s()",
+ func_name)));
+}
+
/*
* Try to evict a shared buffer.
*/
Datum
pg_buffercache_evict(PG_FUNCTION_ARGS)
{
+ Datum result;
+ TupleDesc tupledesc;
+ HeapTuple tuple;
+ Datum values[NUM_BUFFERCACHE_EVICT_ELEM];
+ bool nulls[NUM_BUFFERCACHE_EVICT_ELEM] = {0};
+
Buffer buf = PG_GETARG_INT32(0);
+ bool buffer_flushed;
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to use pg_buffercache_evict function")));
+ if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
+
+ pg_buffercache_superuser_check("pg_buffercache_evict");
if (buf < 1 || buf > NBuffers)
elog(ERROR, "bad buffer ID: %d", buf);
- PG_RETURN_BOOL(EvictUnpinnedBuffer(buf));
+ values[0] = BoolGetDatum(EvictUnpinnedBuffer(buf, &buffer_flushed));
+ values[1] = BoolGetDatum(buffer_flushed);
+
+ tuple = heap_form_tuple(tupledesc, values, nulls);
+ result = HeapTupleGetDatum(tuple);
+
+ PG_RETURN_DATUM(result);
+}
+
+/*
+ * Try to evict specified relation.
+ */
+Datum
+pg_buffercache_evict_relation(PG_FUNCTION_ARGS)
+{
+ Datum result;
+ TupleDesc tupledesc;
+ HeapTuple tuple;
+ Datum values[NUM_BUFFERCACHE_EVICT_RELATION_ELEM];
+ bool nulls[NUM_BUFFERCACHE_EVICT_RELATION_ELEM] = {0};
+
+ Oid relOid;
+ Relation rel;
+
+ int32 buffers_evicted = 0;
+ int32 buffers_flushed = 0;
+ int32 buffers_skipped = 0;
+
+ if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
+
+ pg_buffercache_superuser_check("pg_buffercache_evict_relation");
+
+ relOid = PG_GETARG_OID(0);
+
+ rel = relation_open(relOid, AccessShareLock);
+
+ if (RelationUsesLocalBuffers(rel))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("relation uses local buffers, %s() is intended to be used for shared buffers only",
+ "pg_buffercache_evict_relation")));
+
+ EvictRelUnpinnedBuffers(rel, &buffers_evicted, &buffers_flushed,
+ &buffers_skipped);
+
+ relation_close(rel, AccessShareLock);
+
+ values[0] = Int32GetDatum(buffers_evicted);
+ values[1] = Int32GetDatum(buffers_flushed);
+ values[2] = Int32GetDatum(buffers_skipped);
+
+ tuple = heap_form_tuple(tupledesc, values, nulls);
+ result = HeapTupleGetDatum(tuple);
+
+ PG_RETURN_DATUM(result);
+}
+
+
+/*
+ * Try to evict all shared buffers.
+ */
+Datum
+pg_buffercache_evict_all(PG_FUNCTION_ARGS)
+{
+ Datum result;
+ TupleDesc tupledesc;
+ HeapTuple tuple;
+ Datum values[NUM_BUFFERCACHE_EVICT_ALL_ELEM];
+ bool nulls[NUM_BUFFERCACHE_EVICT_ALL_ELEM] = {0};
+
+ int32 buffers_evicted = 0;
+ int32 buffers_flushed = 0;
+ int32 buffers_skipped = 0;
+
+ if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
+
+ pg_buffercache_superuser_check("pg_buffercache_evict_all");
+
+ EvictAllUnpinnedBuffers(&buffers_evicted, &buffers_flushed,
+ &buffers_skipped);
+
+ values[0] = Int32GetDatum(buffers_evicted);
+ values[1] = Int32GetDatum(buffers_flushed);
+ values[2] = Int32GetDatum(buffers_skipped);
+
+ tuple = heap_form_tuple(tupledesc, values, nulls);
+ result = HeapTupleGetDatum(tuple);
+
+ PG_RETURN_DATUM(result);
}
SELECT count(*) > 0 FROM pg_buffercache;
SELECT buffers_used + buffers_unused > 0 FROM pg_buffercache_summary();
SELECT count(*) > 0 FROM pg_buffercache_usage_counts();
+RESET role;
+
+
+------
+---- Test pg_buffercache_evict* functions
+------
+
+CREATE ROLE regress_buffercache_normal;
+SET ROLE regress_buffercache_normal;
+
+-- These should fail because they need to be called as SUPERUSER
+SELECT * FROM pg_buffercache_evict(1);
+SELECT * FROM pg_buffercache_evict_relation(1);
+SELECT * FROM pg_buffercache_evict_all();
+
+RESET ROLE;
+
+-- These should return nothing, because these are STRICT functions
+SELECT * FROM pg_buffercache_evict(NULL);
+SELECT * FROM pg_buffercache_evict_relation(NULL);
+
+-- These should fail because they are not called by valid range of buffers
+-- Number of the shared buffers are limited by max integer
+SELECT 2147483647 max_buffers \gset
+SELECT * FROM pg_buffercache_evict(-1);
+SELECT * FROM pg_buffercache_evict(0);
+SELECT * FROM pg_buffercache_evict(:max_buffers);
+
+-- This should fail because pg_buffercache_evict_relation() doesn't accept
+-- local relations
+CREATE TEMP TABLE temp_pg_buffercache();
+SELECT * FROM pg_buffercache_evict_relation('temp_pg_buffercache');
+DROP TABLE temp_pg_buffercache;
+
+-- These shouldn't fail
+SELECT buffer_evicted IS NOT NULL FROM pg_buffercache_evict(1);
+SELECT buffers_evicted IS NOT NULL FROM pg_buffercache_evict_all();
+CREATE TABLE shared_pg_buffercache();
+SELECT buffers_evicted IS NOT NULL FROM pg_buffercache_evict_relation('shared_pg_buffercache');
+DROP TABLE shared_pg_buffercache;
+
+DROP ROLE regress_buffercache_normal;
<primary>pg_buffercache_evict</primary>
</indexterm>
+ <indexterm>
+ <primary>pg_buffercache_evict_relation</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>pg_buffercache_evict_all</primary>
+ </indexterm>
+
<para>
This module provides the <function>pg_buffercache_pages()</function>
function (wrapped in the <structname>pg_buffercache</structname> view),
<function>pg_buffercache_numa_pages()</function> function (wrapped in the
<structname>pg_buffercache_numa</structname> view), the
<function>pg_buffercache_summary()</function> function, the
- <function>pg_buffercache_usage_counts()</function> function and
- the <function>pg_buffercache_evict()</function> function.
+ <function>pg_buffercache_usage_counts()</function> function, the
+ <function>pg_buffercache_evict()</function>, the
+ <function>pg_buffercache_evict_relation()</function> function and the
+ <function>pg_buffercache_evict_all()</function> function.
</para>
<para>
function is restricted to superusers only.
</para>
+ <para>
+ The <function>pg_buffercache_evict_relation()</function> function allows all
+ unpinned shared buffers in the relation to be evicted from the buffer pool
+ given a relation identifier. Use of this function is restricted to
+ superusers only.
+ </para>
+
+ <para>
+ The <function>pg_buffercache_evict_all()</function> function allows all
+ unpinned shared buffers to be evicted in the buffer pool. Use of this
+ function is restricted to superusers only.
+ </para>
+
<sect2 id="pgbuffercache-pg-buffercache">
<title>The <structname>pg_buffercache</structname> View</title>
<para>
The <function>pg_buffercache_evict()</function> function takes a buffer
identifier, as shown in the <structfield>bufferid</structfield> column of
- the <structname>pg_buffercache</structname> view. It returns true on success,
- and false if the buffer wasn't valid, if it couldn't be evicted because it
- was pinned, or if it became dirty again after an attempt to write it out.
- The result is immediately out of date upon return, as the buffer might
- become valid again at any time due to concurrent activity. The function is
+ the <structname>pg_buffercache</structname> view. It returns information
+ about whether the buffer was evicted and flushed. The buffer_evicted
+ column is true on success, and false if the buffer wasn't valid, if it
+ couldn't be evicted because it was pinned, or if it became dirty again
+ after an attempt to write it out. The buffer_flushed column is true if the
+ buffer was flushed. This does not necessarily mean that buffer was flushed
+ by us, it might be flushed by someone else. The result is immediately out
+ of date upon return, as the buffer might become valid again at any time due
+ to concurrent activity. The function is intended for developer testing
+ only.
+ </para>
+ </sect2>
+
+ <sect2 id="pgbuffercache-pg-buffercache-evict-relation">
+ <title>The <structname>pg_buffercache_evict_relation</structname> Function</title>
+ <para>
+ The <function>pg_buffercache_evict_relation()</function> function is very
+ similar to the <function>pg_buffercache_evict()</function> function. The
+ difference is that the <function>pg_buffercache_evict_relation()</function>
+ takes a relation identifier instead of buffer identifier. It tries to
+ evict all buffers for all forks in that relation.
+
+ It returns the number of evicted buffers, flushed buffers and the number of
+ buffers that could not be evicted. Flushed buffers haven't necessarily
+ been flushed by us, they might have been flushed by someone else. The
+ result is immediately out of date upon return, as buffers might immediately
+ be read back in due to concurrent activity. The function is intended for
+ developer testing only.
+ </para>
+ </sect2>
+
+ <sect2 id="pgbuffercache-pg-buffercache-evict-all">
+ <title>The <structname>pg_buffercache_evict_all</structname> Function</title>
+ <para>
+ The <function>pg_buffercache_evict_all()</function> function is very
+ similar to the <function>pg_buffercache_evict()</function> function. The
+ difference is, the <function>pg_buffercache_evict_all()</function> function
+ does not take an argument; instead it tries to evict all buffers in the
+ buffer pool. It returns the number of evicted buffers, flushed buffers and
+ the number of buffers that could not be evicted. Flushed buffers haven't
+ necessarily been flushed by us, they might have been flushed by someone
+ else. The result is immediately out of date upon return, as buffers might
+ immediately be read back in due to concurrent activity. The function is
intended for developer testing only.
</para>
</sect2>
}
/*
- * Try to evict the current block in a shared buffer.
- *
- * This function is intended for testing/development use only!
- *
- * To succeed, the buffer must not be pinned on entry, so if the caller had a
- * particular block in mind, it might already have been replaced by some other
- * block by the time this function runs. It's also unpinned on return, so the
- * buffer might be occupied again by the time control is returned, potentially
- * even by the same block. This inherent raciness without other interlocking
- * makes the function unsuitable for non-testing usage.
- *
- * Returns true if the buffer was valid and it has now been made invalid.
- * Returns false if it wasn't valid, if it couldn't be evicted due to a pin,
- * or if the buffer becomes dirty again while we're trying to write it out.
+ * Helper function to evict unpinned buffer whose buffer header lock is
+ * already acquired.
*/
-bool
-EvictUnpinnedBuffer(Buffer buf)
+static bool
+EvictUnpinnedBufferInternal(BufferDesc *desc, bool *buffer_flushed)
{
- BufferDesc *desc;
uint32 buf_state;
bool result;
- /* Make sure we can pin the buffer. */
- ResourceOwnerEnlarge(CurrentResourceOwner);
- ReservePrivateRefCountEntry();
+ *buffer_flushed = false;
- Assert(!BufferIsLocal(buf));
- desc = GetBufferDescriptor(buf - 1);
+ buf_state = pg_atomic_read_u32(&(desc->state));
+ Assert(buf_state & BM_LOCKED);
- /* Lock the header and check if it's valid. */
- buf_state = LockBufHdr(desc);
if ((buf_state & BM_VALID) == 0)
{
UnlockBufHdr(desc, buf_state);
{
LWLockAcquire(BufferDescriptorGetContentLock(desc), LW_SHARED);
FlushBuffer(desc, NULL, IOOBJECT_RELATION, IOCONTEXT_NORMAL);
+ *buffer_flushed = true;
LWLockRelease(BufferDescriptorGetContentLock(desc));
}
return result;
}
+/*
+ * Try to evict the current block in a shared buffer.
+ *
+ * This function is intended for testing/development use only!
+ *
+ * To succeed, the buffer must not be pinned on entry, so if the caller had a
+ * particular block in mind, it might already have been replaced by some other
+ * block by the time this function runs. It's also unpinned on return, so the
+ * buffer might be occupied again by the time control is returned, potentially
+ * even by the same block. This inherent raciness without other interlocking
+ * makes the function unsuitable for non-testing usage.
+ *
+ * *buffer_flushed is set to true if the buffer was dirty and has been
+ * flushed, false otherwise. However, *buffer_flushed=true does not
+ * necessarily mean that we flushed the buffer, it could have been flushed by
+ * someone else.
+ *
+ * Returns true if the buffer was valid and it has now been made invalid.
+ * Returns false if it wasn't valid, if it couldn't be evicted due to a pin,
+ * or if the buffer becomes dirty again while we're trying to write it out.
+ */
+bool
+EvictUnpinnedBuffer(Buffer buf, bool *buffer_flushed)
+{
+ BufferDesc *desc;
+
+ Assert(BufferIsValid(buf) && !BufferIsLocal(buf));
+
+ /* Make sure we can pin the buffer. */
+ ResourceOwnerEnlarge(CurrentResourceOwner);
+ ReservePrivateRefCountEntry();
+
+ desc = GetBufferDescriptor(buf - 1);
+ LockBufHdr(desc);
+
+ return EvictUnpinnedBufferInternal(desc, buffer_flushed);
+}
+
+/*
+ * Try to evict all the shared buffers.
+ *
+ * This function is intended for testing/development use only! See
+ * EvictUnpinnedBuffer().
+ *
+ * The buffers_* parameters are mandatory and indicate the total count of
+ * buffers that:
+ * - buffers_evicted - were evicted
+ * - buffers_flushed - were flushed
+ * - buffers_skipped - could not be evicted
+ */
+void
+EvictAllUnpinnedBuffers(int32 *buffers_evicted, int32 *buffers_flushed,
+ int32 *buffers_skipped)
+{
+ *buffers_evicted = 0;
+ *buffers_skipped = 0;
+ *buffers_flushed = 0;
+
+ for (int buf = 1; buf <= NBuffers; buf++)
+ {
+ BufferDesc *desc = GetBufferDescriptor(buf - 1);
+ uint32 buf_state;
+ bool buffer_flushed;
+
+ buf_state = pg_atomic_read_u32(&desc->state);
+ if (!(buf_state & BM_VALID))
+ continue;
+
+ ResourceOwnerEnlarge(CurrentResourceOwner);
+ ReservePrivateRefCountEntry();
+
+ LockBufHdr(desc);
+
+ if (EvictUnpinnedBufferInternal(desc, &buffer_flushed))
+ (*buffers_evicted)++;
+ else
+ (*buffers_skipped)++;
+
+ if (buffer_flushed)
+ (*buffers_flushed)++;
+ }
+}
+
+/*
+ * Try to evict all the shared buffers containing provided relation's pages.
+ *
+ * This function is intended for testing/development use only! See
+ * EvictUnpinnedBuffer().
+ *
+ * The caller must hold at least AccessShareLock on the relation to prevent
+ * the relation from being dropped.
+ *
+ * The buffers_* parameters are mandatory and indicate the total count of
+ * buffers that:
+ * - buffers_evicted - were evicted
+ * - buffers_flushed - were flushed
+ * - buffers_skipped - could not be evicted
+ */
+void
+EvictRelUnpinnedBuffers(Relation rel, int32 *buffers_evicted,
+ int32 *buffers_flushed, int32 *buffers_skipped)
+{
+ Assert(!RelationUsesLocalBuffers(rel));
+
+ *buffers_skipped = 0;
+ *buffers_evicted = 0;
+ *buffers_flushed = 0;
+
+ for (int buf = 1; buf <= NBuffers; buf++)
+ {
+ BufferDesc *desc = GetBufferDescriptor(buf - 1);
+ uint32 buf_state = pg_atomic_read_u32(&(desc->state));
+ bool buffer_flushed;
+
+ /* An unlocked precheck should be safe and saves some cycles. */
+ if ((buf_state & BM_VALID) == 0 ||
+ !BufTagMatchesRelFileLocator(&desc->tag, &rel->rd_locator))
+ continue;
+
+ /* Make sure we can pin the buffer. */
+ ResourceOwnerEnlarge(CurrentResourceOwner);
+ ReservePrivateRefCountEntry();
+
+ buf_state = LockBufHdr(desc);
+
+ /* recheck, could have changed without the lock */
+ if ((buf_state & BM_VALID) == 0 ||
+ !BufTagMatchesRelFileLocator(&desc->tag, &rel->rd_locator))
+ {
+ UnlockBufHdr(desc, buf_state);
+ continue;
+ }
+
+ if (EvictUnpinnedBufferInternal(desc, &buffer_flushed))
+ (*buffers_evicted)++;
+ else
+ (*buffers_skipped)++;
+
+ if (buffer_flushed)
+ (*buffers_flushed)++;
+ }
+}
+
/*
* Generic implementation of the AIO handle staging callback for readv/writev
* on local/shared buffers.
extern void LimitAdditionalPins(uint32 *additional_pins);
extern void LimitAdditionalLocalPins(uint32 *additional_pins);
-extern bool EvictUnpinnedBuffer(Buffer buf);
+extern bool EvictUnpinnedBuffer(Buffer buf, bool *buffer_flushed);
+extern void EvictAllUnpinnedBuffers(int32 *buffers_evicted,
+ int32 *buffers_flushed,
+ int32 *buffers_skipped);
+extern void EvictRelUnpinnedBuffers(Relation rel,
+ int32 *buffers_evicted,
+ int32 *buffers_flushed,
+ int32 *buffers_skipped);
/* in buf_init.c */
extern void BufferManagerShmemInit(void);
bool corrupt_header = PG_GETARG_BOOL(3);
bool corrupt_checksum = PG_GETARG_BOOL(4);
Page page = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+ bool flushed;
Relation rel;
Buffer buf;
PageHeader ph;
if (BufferIsLocal(buf))
InvalidateLocalBuffer(GetLocalBufferDescriptor(-buf - 1), true);
else
- EvictUnpinnedBuffer(buf);
+ EvictUnpinnedBuffer(buf, &flushed);
/*
* Now modify the page as asked for by the caller.
BufferDesc *buf_hdr = BufferIsLocal(buf) ?
GetLocalBufferDescriptor(-buf - 1)
: GetBufferDescriptor(buf - 1);
+ bool flushed;
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
if (BufferIsLocal(buf))
InvalidateLocalBuffer(GetLocalBufferDescriptor(-buf - 1), true);
- else if (!EvictUnpinnedBuffer(buf))
+ else if (!EvictUnpinnedBuffer(buf, &flushed))
elog(ERROR, "couldn't evict");
}
}