From 26aaed974a81ec61cb9510e6ca7266f8b53bd1fa Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilip.kumar@enterprisedb.com>
Date: Thu, 13 Jun 2019 09:19:47 +0530
Subject: [PATCH 08/14] Test module for undo api

Basic test routine to test undo interface API.

Dilip Kumar
---
 src/test/modules/test_undo_api/Makefile            |  21 +++
 .../test_undo_api/expected/test_undo_api.out       |  12 ++
 .../modules/test_undo_api/sql/test_undo_api.sql    |   8 +
 .../modules/test_undo_api/test_undo_api--1.0.sql   |   6 +
 src/test/modules/test_undo_api/test_undo_api.c     | 185 +++++++++++++++++++++
 .../modules/test_undo_api/test_undo_api.control    |   4 +
 6 files changed, 236 insertions(+)
 create mode 100644 src/test/modules/test_undo_api/Makefile
 create mode 100644 src/test/modules/test_undo_api/expected/test_undo_api.out
 create mode 100644 src/test/modules/test_undo_api/sql/test_undo_api.sql
 create mode 100644 src/test/modules/test_undo_api/test_undo_api--1.0.sql
 create mode 100644 src/test/modules/test_undo_api/test_undo_api.c
 create mode 100644 src/test/modules/test_undo_api/test_undo_api.control

diff --git a/src/test/modules/test_undo_api/Makefile b/src/test/modules/test_undo_api/Makefile
new file mode 100644
index 0000000..deb3816
--- /dev/null
+++ b/src/test/modules/test_undo_api/Makefile
@@ -0,0 +1,21 @@
+# src/test/modules/test_undo/Makefile
+
+MODULE_big = test_undo_api
+OBJS = test_undo_api.o
+PGFILEDESC = "test_undo_api - a test module for the undo api layer"
+
+EXTENSION = test_undo_api
+DATA = test_undo_api--1.0.sql
+
+REGRESS = test_undo_api
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_undo_api
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_undo_api/expected/test_undo_api.out b/src/test/modules/test_undo_api/expected/test_undo_api.out
new file mode 100644
index 0000000..5496ddb
--- /dev/null
+++ b/src/test/modules/test_undo_api/expected/test_undo_api.out
@@ -0,0 +1,12 @@
+CREATE EXTENSION test_undo_api;
+--
+-- This test will insert the data in the undo using undo api and after that
+-- it will fetch the data and verify that whether we have got the same data
+-- back or not.
+--
+SELECT test_undo_api('permanent');
+ test_undo_api 
+---------------
+ 
+(1 row)
+
diff --git a/src/test/modules/test_undo_api/sql/test_undo_api.sql b/src/test/modules/test_undo_api/sql/test_undo_api.sql
new file mode 100644
index 0000000..fa6f789
--- /dev/null
+++ b/src/test/modules/test_undo_api/sql/test_undo_api.sql
@@ -0,0 +1,8 @@
+CREATE EXTENSION test_undo_api;
+
+--
+-- This test will insert the data in the undo using undo api and after that
+-- it will fetch the data and verify that whether we have got the same data
+-- back or not.
+--
+SELECT test_undo_api('permanent');
diff --git a/src/test/modules/test_undo_api/test_undo_api--1.0.sql b/src/test/modules/test_undo_api/test_undo_api--1.0.sql
new file mode 100644
index 0000000..1aa4e02
--- /dev/null
+++ b/src/test/modules/test_undo_api/test_undo_api--1.0.sql
@@ -0,0 +1,6 @@
+\echo Use "CREATE EXTENSION test_undo_api" to load this file. \quit
+
+CREATE FUNCTION test_undo_api(persistence text)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
diff --git a/src/test/modules/test_undo_api/test_undo_api.c b/src/test/modules/test_undo_api/test_undo_api.c
new file mode 100644
index 0000000..396714b
--- /dev/null
+++ b/src/test/modules/test_undo_api/test_undo_api.c
@@ -0,0 +1,185 @@
+#include "postgres.h"
+
+#include "access/transam.h"
+#include "access/undoaccess.h"
+#include "catalog/pg_class.h"
+#include "fmgr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "utils/builtins.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(test_undo_api);
+
+static void
+compare_undo_record(UnpackedUndoRecord *urp1, UnpackedUndoRecord *urp2)
+{
+	int	header_size = offsetof(UnpackedUndoRecord, uur_next) + sizeof(uint64);
+
+	/* Compare undo record header. */
+	if (strncmp((char *) urp1, (char *) urp2, header_size) != 0)
+		elog(ERROR, "undo header did not match");
+
+	/* Compare payload and tuple length. */
+	if (urp1->uur_payload.len != urp2->uur_payload.len)
+		elog(ERROR, "payload data length did not match");
+
+	if (urp1->uur_tuple.len != urp2->uur_tuple.len)
+		elog(ERROR, "tuple data length did not match");
+
+	/* Compare undo record payload data. */
+	if (strncmp(urp1->uur_payload.data, urp2->uur_payload.data, urp1->uur_tuple.len) != 0)
+		elog(ERROR, "undo payload data did not match");
+
+	/* Compare undo record tuple data. */
+	if (strncmp(urp1->uur_tuple.data, urp2->uur_tuple.data, urp1->uur_tuple.len) != 0)
+		elog(ERROR, "undo tuple data did not match");
+}
+
+/*
+ * test_insert_and_fetch - test simple insert and fetch undo record API
+ */
+static void
+test_insert_and_fetch()
+{
+	UndoRecordInsertContext context = {{0}};
+	UndoPersistence persistence = UNDO_PERMANENT;
+	char	data[5000];
+	int		 len = 5000;
+	UnpackedUndoRecord	undorecord = {0};
+	UnpackedUndoRecord *undorecord_out;
+	UndoRecPtr	undo_ptr;
+
+	/* Prepare dummy undo record*/
+	undorecord.uur_rmid = 1;
+	undorecord.uur_type = 2;
+	undorecord.uur_info = 0;
+	undorecord.uur_xid = 100;
+	undorecord.uur_cid = 1;
+	undorecord.uur_fork = MAIN_FORKNUM;
+	undorecord.uur_blkprev = 10;
+	undorecord.uur_block = 1;
+	undorecord.uur_offset = 10;
+
+	/* Insert large data so that record get split across pages. */
+	initStringInfo(&undorecord.uur_tuple);
+	memset(data, 'a', 5000);
+	appendBinaryStringInfo(&undorecord.uur_tuple,
+						   (char *) data,
+						   len);
+	initStringInfo(&undorecord.uur_payload);
+	appendBinaryStringInfo(&undorecord.uur_payload,
+						   (char *) data,
+						   len);
+	/* Prepare undo record. */
+	BeginUndoRecordInsert(&context, persistence, 2, NULL);
+	undo_ptr = PrepareUndoInsert(&context, &undorecord, InvalidFullTransactionId);
+
+	/* Insert prepared undo record under critical section. */
+	START_CRIT_SECTION();
+	InsertPreparedUndo(&context);
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+
+	/* Fetch inserted undo record. */
+	undorecord_out = UndoFetchRecord(undo_ptr, InvalidBlockNumber,
+									 InvalidOffsetNumber,
+									 InvalidTransactionId, NULL,
+									 NULL);
+	/* compare undo records. */
+	compare_undo_record(&undorecord, undorecord_out);
+
+	UndoRecordRelease(undorecord_out);
+	pfree(undorecord.uur_tuple.data);
+}
+
+#define MAX_UNDO_RECORD 10
+/*
+ * test_bulk_fetch - test the bulk fetch API.
+ */
+static void
+test_bulk_fetch()
+{
+	int i;
+	UndoRecordInsertContext context = {{0}};
+	UndoPersistence persistence = UNDO_PERMANENT;
+	UndoRecInfo	urp_in_array[MAX_UNDO_RECORD];
+	UndoRecInfo *urp_out_array;
+	UnpackedUndoRecord	uur[MAX_UNDO_RECORD] = {{0}};
+	UndoRecPtr	undo_ptr;
+	int			nrecords = 0;
+
+	for (i = 0; i < MAX_UNDO_RECORD; i++)
+	{
+		uur[i].uur_rmid = 1;
+		uur[i].uur_type = 2;
+		uur[i].uur_info = 0;
+		uur[i].uur_xid = 100;
+		uur[i].uur_cid = 1;
+		uur[i].uur_fork = MAIN_FORKNUM;
+		uur[i].uur_blkprev = 10;
+		uur[i].uur_block = i;
+		uur[i].uur_offset = i + 1;
+		urp_in_array[i].uur = &uur[i];
+	}
+
+	/* Prepare multiple undo records. */
+	BeginUndoRecordInsert(&context, persistence, MAX_UNDO_RECORD, NULL);
+	for (i = 0; i < MAX_UNDO_RECORD; i++)
+	{
+		undo_ptr = PrepareUndoInsert(&context, &uur[i], InvalidFullTransactionId);
+		urp_in_array[i].urp = undo_ptr;
+	}
+
+	/* Insert them all in one shot. */
+	START_CRIT_SECTION();
+	InsertPreparedUndo(&context);
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+
+	undo_ptr = urp_in_array[MAX_UNDO_RECORD - 1].urp;
+
+	/*
+	 * Perform the bulk fetch. 2000 bytes are enough to hold 10 records.  Later
+	 * we can enhance this to test the fetch in multi batch by increasing the
+	 * record counts or reducing undo_apply_size to smaller value.
+	 */
+	urp_out_array = UndoBulkFetchRecord(&undo_ptr, urp_in_array[0].urp, 2000,
+										&nrecords, false);
+	/* Check whether we have got all the record we inserted. */
+	if (nrecords != MAX_UNDO_RECORD)
+		elog(ERROR, "undo record count did not match");
+
+	/* Compare all records we have fetch using bulk fetch API*/
+	for (i = 0; i < MAX_UNDO_RECORD; i++)
+	{
+		if (urp_in_array[i].urp != urp_out_array[MAX_UNDO_RECORD - 1 - i].urp)
+			elog(ERROR, "undo record pointer did not match");
+		compare_undo_record(urp_in_array[i].uur, urp_out_array[MAX_UNDO_RECORD - 1 - i].uur);
+		UndoRecordRelease(urp_out_array[MAX_UNDO_RECORD - 1 - i].uur);
+	}
+}
+/*
+ * Undo API test module
+ */
+Datum
+test_undo_api(PG_FUNCTION_ARGS)
+{
+	/* Test simple insert and fetch record. */
+	test_insert_and_fetch();
+
+	/* Test undo record bulk fetch API*/
+	test_bulk_fetch();
+
+	PG_RETURN_VOID();
+}
diff --git a/src/test/modules/test_undo_api/test_undo_api.control b/src/test/modules/test_undo_api/test_undo_api.control
new file mode 100644
index 0000000..09df344
--- /dev/null
+++ b/src/test/modules/test_undo_api/test_undo_api.control
@@ -0,0 +1,4 @@
+comment = 'test_undo_api'
+default_version = '1.0'
+module_pathname = '$libdir/test_undo_api'
+relocatable = true
-- 
1.8.3.1

