From 9e27451318e4a4a2e9061e79b5139560610bd78a Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Thu, 28 Mar 2024 20:21:25 +0200
Subject: [PATCH v13-bmr 4/5] Add readbufferbench

---
 src/test/modules/Makefile                     |  1 +
 src/test/modules/meson.build                  |  1 +
 src/test/modules/readbufferbench/Makefile     | 21 ++++
 src/test/modules/readbufferbench/bench.sql    | 20 ++++
 src/test/modules/readbufferbench/meson.build  | 28 ++++++
 .../readbufferbench/readbufferbench--1.0.sql  |  9 ++
 .../modules/readbufferbench/readbufferbench.c | 98 +++++++++++++++++++
 .../readbufferbench/readbufferbench.control   |  4 +
 8 files changed, 182 insertions(+)
 create mode 100644 src/test/modules/readbufferbench/Makefile
 create mode 100644 src/test/modules/readbufferbench/bench.sql
 create mode 100644 src/test/modules/readbufferbench/meson.build
 create mode 100644 src/test/modules/readbufferbench/readbufferbench--1.0.sql
 create mode 100644 src/test/modules/readbufferbench/readbufferbench.c
 create mode 100644 src/test/modules/readbufferbench/readbufferbench.control

diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 1cbd532156d..6c96a83f60e 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -12,6 +12,7 @@ SUBDIRS = \
 		  dummy_seclabel \
 		  libpq_pipeline \
 		  plsample \
+		  readbufferbench \
 		  spgist_name_ops \
 		  test_bloomfilter \
 		  test_copy_callbacks \
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index 7c11fb97f21..8ad342bcc73 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -10,6 +10,7 @@ subdir('injection_points')
 subdir('ldap_password_func')
 subdir('libpq_pipeline')
 subdir('plsample')
+subdir('readbufferbench')
 subdir('spgist_name_ops')
 subdir('ssl_passphrase_callback')
 subdir('test_bloomfilter')
diff --git a/src/test/modules/readbufferbench/Makefile b/src/test/modules/readbufferbench/Makefile
new file mode 100644
index 00000000000..8b421b1cb1d
--- /dev/null
+++ b/src/test/modules/readbufferbench/Makefile
@@ -0,0 +1,21 @@
+# src/test/modules/readbufferbench/Makefile
+
+MODULE_big = readbufferbench
+OBJS = \
+	$(WIN32RES) \
+	readbufferbench.o
+PGFILEDESC = "readbufferbench - micro-benchmarking code ReadBuffer and friends"
+
+EXTENSION = readbufferbench
+DATA = readbufferbench--1.0.sql
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/readbufferbench
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/readbufferbench/bench.sql b/src/test/modules/readbufferbench/bench.sql
new file mode 100644
index 00000000000..76b6be5f3e1
--- /dev/null
+++ b/src/test/modules/readbufferbench/bench.sql
@@ -0,0 +1,20 @@
+drop table foo;
+create table foo (i integer) with (fillfactor=10);
+insert into foo select g from generate_series(1, 100000) g;
+
+do $$
+declare
+  begints timestamptz;
+  endts timestamptz;
+  results float[];
+  i int;
+begin
+  for i in 1..5 loop
+    begints = clock_timestamp();
+    perform readbuffer_bench('foo', 10, 10000000);
+    endts = clock_timestamp();
+    results[i] = extract(epoch from endts) - extract(epoch from begints);
+    raise notice 'run %: %', i, results[i];
+  end loop;
+end;
+$$;
diff --git a/src/test/modules/readbufferbench/meson.build b/src/test/modules/readbufferbench/meson.build
new file mode 100644
index 00000000000..1b7e7863c26
--- /dev/null
+++ b/src/test/modules/readbufferbench/meson.build
@@ -0,0 +1,28 @@
+# Copyright (c) 2022-2024, PostgreSQL Global Development Group
+
+readbufferbench_sources = files(
+  'readbufferbench.c',
+)
+
+if host_system == 'windows'
+  readbufferbench_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+    '--NAME', 'readbufferbench',
+    '--FILEDESC', 'readbufferbench - micro-benchmarking code ReadBuffer and friends',])
+endif
+
+readbufferbench = shared_module('readbufferbench',
+  readbufferbench_sources,
+  kwargs: pg_test_mod_args,
+)
+test_install_libs += readbufferbench
+
+test_install_data += files(
+  'readbufferbench.control',
+  'readbufferbench--1.0.sql',
+)
+
+tests += {
+  'name': 'readbufferbench',
+  'sd': meson.current_source_dir(),
+  'bd': meson.current_build_dir(),
+}
diff --git a/src/test/modules/readbufferbench/readbufferbench--1.0.sql b/src/test/modules/readbufferbench/readbufferbench--1.0.sql
new file mode 100644
index 00000000000..0ab5a4015a3
--- /dev/null
+++ b/src/test/modules/readbufferbench/readbufferbench--1.0.sql
@@ -0,0 +1,9 @@
+/* src/test/modules/readbufferbench/readbufferbench--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION readbufferbench" to load this file. \quit
+
+
+CREATE FUNCTION readbuffer_bench(regclass, int4, int4)
+RETURNS void
+AS 'MODULE_PATHNAME' LANGUAGE C STRICT PARALLEL SAFE;
diff --git a/src/test/modules/readbufferbench/readbufferbench.c b/src/test/modules/readbufferbench/readbufferbench.c
new file mode 100644
index 00000000000..8d4c8281c29
--- /dev/null
+++ b/src/test/modules/readbufferbench/readbufferbench.c
@@ -0,0 +1,98 @@
+/*--------------------------------------------------------------------------
+ *
+ * readbufferbench.c
+ *		Test false positive rate of Bloom filter.
+ *
+ * Copyright (c) 2018-2024, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *		src/test/modules/readbufferbench/readbufferbench.c
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/relation.h"
+#include "fmgr.h"
+#include "miscadmin.h"
+#include "storage/bufmgr.h"
+#include "utils/rel.h"
+
+PG_MODULE_MAGIC;
+
+
+PG_FUNCTION_INFO_V1(readbuffer_bench);
+
+Datum
+readbuffer_bench(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	int32		nblocks = PG_GETARG_INT32(1);
+	uint32		niters = PG_GETARG_UINT32(2);
+	Relation	rel;
+	Buffer		buf;
+
+	if (PG_NARGS() != 3)
+		elog(ERROR, "wrong number of arguments");
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser")));
+
+	rel = relation_open(relid, AccessShareLock);
+
+	if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("cannot get page from relation \"%s\"",
+						RelationGetRelationName(rel)),
+				 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
+
+	/*
+	 * Reject attempts to read non-local temporary relations; we would be
+	 * likely to get wrong data since we have no visibility into the owning
+	 * session's local buffers.
+	 */
+	if (RELATION_IS_OTHER_TEMP(rel))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary tables of other sessions")));
+
+	if (nblocks < 0)
+		nblocks = RelationGetNumberOfBlocksInFork(rel, MAIN_FORKNUM);
+	else if (nblocks > RelationGetNumberOfBlocksInFork(rel, MAIN_FORKNUM))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("relation has only %d blocks",
+						nblocks)));
+
+#if 1
+	{
+		BufferManagerRelation bmr;
+
+		InitBMRForRel(&bmr, rel, MAIN_FORKNUM, NULL);
+		for (int i = 0; i < niters; i++)
+		{
+			for (BlockNumber blkno = 0; blkno < nblocks; blkno++)
+			{
+				buf = ReadBufferBMR(&bmr, blkno, RBM_NORMAL);
+				ReleaseBuffer(buf);
+			}
+		}
+	}
+#else
+	for (int i = 0; i < niters; i++)
+	{
+		for (BlockNumber blkno = 0; blkno < nblocks; blkno++)
+		{
+			buf = ReadBuffer(rel, blkno);
+			ReleaseBuffer(buf);
+		}
+	}
+#endif
+
+	relation_close(rel, AccessShareLock);
+
+	PG_RETURN_VOID();
+}
diff --git a/src/test/modules/readbufferbench/readbufferbench.control b/src/test/modules/readbufferbench/readbufferbench.control
new file mode 100644
index 00000000000..c7488504c86
--- /dev/null
+++ b/src/test/modules/readbufferbench/readbufferbench.control
@@ -0,0 +1,4 @@
+comment = 'Micro-benchmark of ReadBuffer and friends'
+default_version = '1.0'
+module_pathname = '$libdir/readbufferbench'
+relocatable = true
-- 
2.39.2

