On Fri, Sep 20, 2019 at 08:58:27PM +0900, Michael Paquier wrote:
> I still need to do an extra pass on the code (particularly the AM
> part), but I think that we could commit that.  Please note that I
> included the fix for the lockmode I sent today so as the patch can be
> tested:
> https://www.postgresql.org/message-id/20190920013831.gd1...@paquier.xyz

I looked at that over the last couple of days, and done as attached.
Well, the actual module is in 0003.  I have added more comments to
document the basic AM calls so as it can easier be used as a template
for some other work, and tweaked a couple of things.  0001 and 0002
are just the patches from the other thread to address the issues with
the lock mode of custom reloptions.
--
Michael
From c0c2f75ae0b4fa3e6959a1157d400e316f40ada0 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Mon, 23 Sep 2019 15:20:37 +0900
Subject: [PATCH v2 1/3] Fix failure with lock mode used for custom relation
 options

Relation options can use a custom lock mode since 47167b7, which has
lowered the lock available for some autovacuum parameters, however it
forgot to consider custom relation options.  This causes failures with
ALTER TABLE SET when changing a custom relation option, as its lock is
not defined.  The existing APIs to define a custom reloption does not
allow to define a custom lock mode, so enforce its initialization to
AccessExclusiveMode which is safe enough in all cases.  An upcoming
patch will extend the existing APIs to allow a custom lock mode to be
defined.

The problem can be reproduced with bloom indexes, so add a test there.

Reported-by: Nikolay Sharplov
Analyzed-by: Thomas Munro, Michael Paquier
Author: Michael Paquier
Reviewed-by: Kuntal Ghosh
Discussion: https://postgr.es/m/20190920013831.gd1...@paquier.xyz
Backpatch-through: 9.6
---
 contrib/bloom/expected/bloom.out       | 1 +
 contrib/bloom/sql/bloom.sql            | 1 +
 src/backend/access/common/reloptions.c | 7 +++++++
 3 files changed, 9 insertions(+)

diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
index 5ab9e34f82..dae12a7d3e 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -5,6 +5,7 @@ CREATE TABLE tst (
 );
 INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
 CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ALTER INDEX bloomidx SET (length=80);
 SET enable_seqscan=on;
 SET enable_bitmapscan=off;
 SET enable_indexscan=off;
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 32755f2b1a..4733e1e705 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -7,6 +7,7 @@ CREATE TABLE tst (
 
 INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
 CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ALTER INDEX bloomidx SET (length=80);
 
 SET enable_seqscan=on;
 SET enable_bitmapscan=off;
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 20f4ed3c38..b59e606771 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -659,6 +659,13 @@ allocate_reloption(bits32 kinds, int type, const char *name, const char *desc)
 	newoption->namelen = strlen(name);
 	newoption->type = type;
 
+	/*
+	 * Set the default lock mode for this option.  There is no actual way
+	 * for a module to enforce it when declaring a custom relation option,
+	 * so just use the highest level, which is safe for all cases.
+	 */
+	newoption->lockmode = AccessExclusiveLock;
+
 	MemoryContextSwitchTo(oldcxt);
 
 	return newoption;
-- 
2.23.0

From c495f03450b6c4727b3d2f44b3509916f0ee73ae Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Mon, 23 Sep 2019 15:28:02 +0900
Subject: [PATCH v2 2/3] Allow definition of lock mode for custom reloptions

Relation options can define a lock mode other than AccessExclusiveMode
since 47167b7, but modules defining custom relation options did not
really have a way to enforce that.  Correct that by extending the
current API set so as modules can define a custom lock mode.

Author: Michael Paquier
Reviewed-by: Kuntal Ghosh
Discussion: https://postgr.es/m/20190920013831.gd1...@paquier.xyz
---
 contrib/bloom/blutils.c                |  6 ++++--
 src/backend/access/common/reloptions.c | 28 +++++++++++---------------
 src/include/access/reloptions.h        | 11 ++++++----
 3 files changed, 23 insertions(+), 22 deletions(-)

diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index cc1670934f..dbb24cb5b2 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -60,7 +60,8 @@ _PG_init(void)
 	/* Option for length of signature */
 	add_int_reloption(bl_relopt_kind, "length",
 					  "Length of signature in bits",
-					  DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH);
+					  DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH,
+					  AccessExclusiveLock);
 	bl_relopt_tab[0].optname = "length";
 	bl_relopt_tab[0].opttype = RELOPT_TYPE_INT;
 	bl_relopt_tab[0].offset = offsetof(BloomOptions, bloomLength);
@@ -71,7 +72,8 @@ _PG_init(void)
 		snprintf(buf, sizeof(buf), "col%d", i + 1);
 		add_int_reloption(bl_relopt_kind, buf,
 						  "Number of bits generated for each index column",
-						  DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS);
+						  DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS,
+						  AccessExclusiveLock);
 		bl_relopt_tab[i + 1].optname = MemoryContextStrdup(TopMemoryContext,
 														   buf);
 		bl_relopt_tab[i + 1].opttype = RELOPT_TYPE_INT;
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index b59e606771..3b8517efea 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -621,7 +621,8 @@ add_reloption(relopt_gen *newoption)
  *		(for types other than string)
  */
 static relopt_gen *
-allocate_reloption(bits32 kinds, int type, const char *name, const char *desc)
+allocate_reloption(bits32 kinds, int type, const char *name, const char *desc,
+				   LOCKMODE lockmode)
 {
 	MemoryContext oldcxt;
 	size_t		size;
@@ -658,13 +659,7 @@ allocate_reloption(bits32 kinds, int type, const char *name, const char *desc)
 	newoption->kinds = kinds;
 	newoption->namelen = strlen(name);
 	newoption->type = type;
-
-	/*
-	 * Set the default lock mode for this option.  There is no actual way
-	 * for a module to enforce it when declaring a custom relation option,
-	 * so just use the highest level, which is safe for all cases.
-	 */
-	newoption->lockmode = AccessExclusiveLock;
+	newoption->lockmode = lockmode;
 
 	MemoryContextSwitchTo(oldcxt);
 
@@ -676,12 +671,13 @@ allocate_reloption(bits32 kinds, int type, const char *name, const char *desc)
  *		Add a new boolean reloption
  */
 void
-add_bool_reloption(bits32 kinds, const char *name, const char *desc, bool default_val)
+add_bool_reloption(bits32 kinds, const char *name, const char *desc,
+				   bool default_val, LOCKMODE lockmode)
 {
 	relopt_bool *newoption;
 
 	newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
-												   name, desc);
+												   name, desc, lockmode);
 	newoption->default_val = default_val;
 
 	add_reloption((relopt_gen *) newoption);
@@ -693,12 +689,12 @@ add_bool_reloption(bits32 kinds, const char *name, const char *desc, bool defaul
  */
 void
 add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
-				  int min_val, int max_val)
+				  int min_val, int max_val, LOCKMODE lockmode)
 {
 	relopt_int *newoption;
 
 	newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
-												  name, desc);
+												  name, desc, lockmode);
 	newoption->default_val = default_val;
 	newoption->min = min_val;
 	newoption->max = max_val;
@@ -712,12 +708,12 @@ add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_
  */
 void
 add_real_reloption(bits32 kinds, const char *name, const char *desc, double default_val,
-				   double min_val, double max_val)
+				   double min_val, double max_val, LOCKMODE lockmode)
 {
 	relopt_real *newoption;
 
 	newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
-												   name, desc);
+												   name, desc, lockmode);
 	newoption->default_val = default_val;
 	newoption->min = min_val;
 	newoption->max = max_val;
@@ -736,7 +732,7 @@ add_real_reloption(bits32 kinds, const char *name, const char *desc, double defa
  */
 void
 add_string_reloption(bits32 kinds, const char *name, const char *desc, const char *default_val,
-					 validate_string_relopt validator)
+					 validate_string_relopt validator, LOCKMODE lockmode)
 {
 	relopt_string *newoption;
 
@@ -745,7 +741,7 @@ add_string_reloption(bits32 kinds, const char *name, const char *desc, const cha
 		(validator) (default_val);
 
 	newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
-													 name, desc);
+													 name, desc, lockmode);
 	newoption->validate_cb = validator;
 	if (default_val)
 	{
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 6d392e4d5a..4b82c6370a 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -247,13 +247,16 @@ typedef struct
 
 extern relopt_kind add_reloption_kind(void);
 extern void add_bool_reloption(bits32 kinds, const char *name, const char *desc,
-							   bool default_val);
+							   bool default_val, LOCKMODE lockmode);
 extern void add_int_reloption(bits32 kinds, const char *name, const char *desc,
-							  int default_val, int min_val, int max_val);
+							  int default_val, int min_val, int max_val,
+							  LOCKMODE lockmode);
 extern void add_real_reloption(bits32 kinds, const char *name, const char *desc,
-							   double default_val, double min_val, double max_val);
+							   double default_val, double min_val, double max_val,
+							   LOCKMODE lockmode);
 extern void add_string_reloption(bits32 kinds, const char *name, const char *desc,
-								 const char *default_val, validate_string_relopt validator);
+								 const char *default_val, validate_string_relopt validator,
+								 LOCKMODE lockmode);
 
 extern Datum transformRelOptions(Datum oldOptions, List *defList,
 								 const char *namspace, char *validnsps[],
-- 
2.23.0

From 2de282f4371786772bfa6bd7a4079e3d3ecadbba Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Tue, 24 Sep 2019 11:37:19 +0900
Subject: [PATCH v2 3/3] Add dummy_index_am to src/test/modules/

This includes for now more tests dedicated to reloptions, bringing the
coverage of this code close to 100%, and the module could be used for
other purposes, like a base template for an index AM implementation.

Author: Nikolay Sharplov
Reviewed-by: Dent John, Michael Paquier
Discussion: https://postgr.es/m/17071942.m9zZutALE6@x200m
---
 src/test/modules/Makefile                     |   1 +
 src/test/modules/dummy_index_am/.gitignore    |   3 +
 src/test/modules/dummy_index_am/Makefile      |  20 ++
 src/test/modules/dummy_index_am/README        |  11 +
 .../dummy_index_am/dummy_index_am--1.0.sql    |  19 ++
 .../modules/dummy_index_am/dummy_index_am.c   | 309 ++++++++++++++++++
 .../dummy_index_am/dummy_index_am.control     |   5 +
 .../dummy_index_am/expected/reloptions.out    |  77 +++++
 .../modules/dummy_index_am/sql/reloptions.sql |  38 +++
 9 files changed, 483 insertions(+)
 create mode 100644 src/test/modules/dummy_index_am/.gitignore
 create mode 100644 src/test/modules/dummy_index_am/Makefile
 create mode 100644 src/test/modules/dummy_index_am/README
 create mode 100644 src/test/modules/dummy_index_am/dummy_index_am--1.0.sql
 create mode 100644 src/test/modules/dummy_index_am/dummy_index_am.c
 create mode 100644 src/test/modules/dummy_index_am/dummy_index_am.control
 create mode 100644 src/test/modules/dummy_index_am/expected/reloptions.out
 create mode 100644 src/test/modules/dummy_index_am/sql/reloptions.sql

diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 0e4e53d63e..b2eaef3bff 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -7,6 +7,7 @@ include $(top_builddir)/src/Makefile.global
 SUBDIRS = \
 		  brin \
 		  commit_ts \
+		  dummy_index_am \
 		  dummy_seclabel \
 		  snapshot_too_old \
 		  test_bloomfilter \
diff --git a/src/test/modules/dummy_index_am/.gitignore b/src/test/modules/dummy_index_am/.gitignore
new file mode 100644
index 0000000000..44d119cfcc
--- /dev/null
+++ b/src/test/modules/dummy_index_am/.gitignore
@@ -0,0 +1,3 @@
+# Generated subdirectories
+/log/
+/results/
diff --git a/src/test/modules/dummy_index_am/Makefile b/src/test/modules/dummy_index_am/Makefile
new file mode 100644
index 0000000000..aaf544a385
--- /dev/null
+++ b/src/test/modules/dummy_index_am/Makefile
@@ -0,0 +1,20 @@
+# src/test/modules/dummy_index_am/Makefile
+
+MODULES = dummy_index_am
+
+EXTENSION = dummy_index_am
+DATA = dummy_index_am--1.0.sql
+PGFILEDESC = "dummy_index_am - index access method template"
+
+REGRESS = reloptions
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/dummy_index_am
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/dummy_index_am/README b/src/test/modules/dummy_index_am/README
new file mode 100644
index 0000000000..7bcdec56f3
--- /dev/null
+++ b/src/test/modules/dummy_index_am/README
@@ -0,0 +1,11 @@
+Dummy Index AM
+==============
+
+Dummy index AM is a module for testing any facility usable by an index
+access method, whose code is kept a maximum simple.
+
+This includes tests for all relation option types:
+- boolean
+- integer
+- real
+- strings (with and without NULL as default)
diff --git a/src/test/modules/dummy_index_am/dummy_index_am--1.0.sql b/src/test/modules/dummy_index_am/dummy_index_am--1.0.sql
new file mode 100644
index 0000000000..005863de87
--- /dev/null
+++ b/src/test/modules/dummy_index_am/dummy_index_am--1.0.sql
@@ -0,0 +1,19 @@
+/* src/test/modules/dummy_index_am/dummy_index_am--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION dummy_index_am" to load this file. \quit
+
+CREATE FUNCTION dihandler(internal)
+RETURNS index_am_handler
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
+
+-- Access method
+CREATE ACCESS METHOD dummy_index_am TYPE INDEX HANDLER dihandler;
+COMMENT ON ACCESS METHOD dummy_index_am IS 'dummy index access method';
+
+-- Operator classes
+CREATE OPERATOR CLASS int4_ops
+DEFAULT FOR TYPE int4 USING dummy_index_am AS
+  OPERATOR 1 = (int4, int4),
+  FUNCTION 1 hashint4(int4);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
new file mode 100644
index 0000000000..1211907e1d
--- /dev/null
+++ b/src/test/modules/dummy_index_am/dummy_index_am.c
@@ -0,0 +1,309 @@
+/*-------------------------------------------------------------------------
+ *
+ * dummy_index_am.c
+ *		Index AM template main file.
+ *
+ * Copyright (c) 2019, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/test/modules/dummy_index_am/dummy_index_am.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/amapi.h"
+#include "access/reloptions.h"
+#include "catalog/index.h"
+#include "nodes/pathnodes.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+
+PG_MODULE_MAGIC;
+
+void		_PG_init(void);
+
+/* parse table for fillRelOptions */
+relopt_parse_elt di_relopt_tab[5];
+
+/* Kind of relation options for dummy index */
+relopt_kind di_relopt_kind;
+
+/* Dummy index options */
+typedef struct DummyIndexOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			option_int;
+	double		option_real;
+	bool		option_bool;
+	char	   *option_string_val;
+	char	   *option_option_null;
+} DummyIndexOptions;
+
+/* Handler for index AM */
+PG_FUNCTION_INFO_V1(dihandler);
+
+/*
+ * Validation function for string relation options.
+ */
+static void
+validate_string_option(const char *value)
+{
+	ereport(NOTICE,
+			(errmsg("new option value for string parameter %s",
+					value ? value : "NULL")));
+}
+
+/*
+ * This function creates a full set of relation option types,
+ * with various patterns.
+ */
+static void
+create_reloptions_table(void)
+{
+	di_relopt_kind = add_reloption_kind();
+
+	add_int_reloption(di_relopt_kind, "option_int",
+					  "Integer option for dummy_index_am",
+					  10, -10, 100, AccessExclusiveLock);
+	di_relopt_tab[0].optname = "option_int";
+	di_relopt_tab[0].opttype = RELOPT_TYPE_INT;
+	di_relopt_tab[0].offset = offsetof(DummyIndexOptions, option_int);
+
+	add_real_reloption(di_relopt_kind, "option_real",
+					   "Real option for dummy_index_am",
+					   3.1415, -10, 100, AccessExclusiveLock);
+	di_relopt_tab[1].optname = "option_real";
+	di_relopt_tab[1].opttype = RELOPT_TYPE_REAL;
+	di_relopt_tab[1].offset = offsetof(DummyIndexOptions, option_real);
+
+	add_bool_reloption(di_relopt_kind, "option_bool",
+					   "Boolean option for dummy_index_am",
+					   true, AccessExclusiveLock);
+	di_relopt_tab[2].optname = "option_bool";
+	di_relopt_tab[2].opttype = RELOPT_TYPE_BOOL;
+	di_relopt_tab[2].offset = offsetof(DummyIndexOptions, option_bool);
+
+	add_string_reloption(di_relopt_kind, "option_string_val",
+						 "String option for dummy_index_am with non-NULL default",
+						 "DefaultValue", &validate_string_option,
+						 AccessExclusiveLock);
+	di_relopt_tab[3].optname = "option_string_val";
+	di_relopt_tab[3].opttype = RELOPT_TYPE_STRING;
+	di_relopt_tab[3].offset = offsetof(DummyIndexOptions, option_string_val);
+
+	/*
+	 * String option for dummy_index_am with NULL default, and without
+	 * description.
+	 */
+	add_string_reloption(di_relopt_kind, "option_string_null",
+						 NULL,	/* description */
+						 NULL, &validate_string_option,
+						 AccessExclusiveLock);
+	di_relopt_tab[4].optname = "option_string_null";
+	di_relopt_tab[4].opttype = RELOPT_TYPE_STRING;
+	di_relopt_tab[4].offset = offsetof(DummyIndexOptions, option_option_null);
+}
+
+
+/*
+ * Build a new index.
+ */
+static IndexBuildResult *
+dibuild(Relation heap, Relation index, IndexInfo *indexInfo)
+{
+	IndexBuildResult *result;
+
+	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
+
+	/* let's pretend that no tuples were scanned */
+	result->heap_tuples = 0;
+	/* and no index tuples were created (that is true) */
+	result->index_tuples = 0;
+
+	return result;
+}
+
+/*
+ * Build an empty index for the initialiation fork.
+ */
+static void
+dibuildempty(Relation index)
+{
+	/* No need to build an init fork for a dummy index */
+}
+
+/*
+ * Insert new tuple to index AM.
+ */
+static bool
+diinsert(Relation index, Datum *values, bool *isnull,
+		 ItemPointer ht_ctid, Relation heapRel,
+		 IndexUniqueCheck checkUnique,
+		 IndexInfo *indexInfo)
+{
+	/* nothing to do */
+	return false;
+}
+
+/*
+ * Bulk deletion of all index entries pointing to a set of table tuples.
+ */
+static IndexBulkDeleteResult *
+dibulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+			 IndexBulkDeleteCallback callback, void *callback_state)
+{
+	/*
+	 * There is nothing to delete.  Return NULL as there is nothing to pass to
+	 * amvacuumcleanup.
+	 */
+	return NULL;
+}
+
+/*
+ * Post-VACUUM cleanup for index AM.
+ */
+static IndexBulkDeleteResult *
+divacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
+{
+	/* Index has not been modified, so returning NULL is fine */
+	return NULL;
+}
+
+/*
+ * Estimate cost of index AM.
+ */
+static void
+dicostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+			   Cost *indexStartupCost, Cost *indexTotalCost,
+			   Selectivity *indexSelectivity, double *indexCorrelation,
+			   double *indexPages)
+{
+	/* Tell planner to never use this index! */
+	*indexStartupCost = 1.0e10;
+	*indexTotalCost = 1.0e10;
+
+	/* Do not care about the rest */
+	*indexSelectivity = 1;
+	*indexCorrelation = 0;
+	*indexPages = 1;
+}
+
+/*
+ * Parse relation options for index AM, returning a DummyIndexOptions
+ * structure filled with option values.
+ */
+static bytea *
+dioptions(Datum reloptions, bool validate)
+{
+	relopt_value *options;
+	int			numoptions;
+	DummyIndexOptions *rdopts;
+
+	/* Parse the user-given reloptions */
+	options = parseRelOptions(reloptions, validate, di_relopt_kind, &numoptions);
+	rdopts = allocateReloptStruct(sizeof(DummyIndexOptions), options, numoptions);
+	fillRelOptions((void *) rdopts, sizeof(DummyIndexOptions), options, numoptions,
+				   validate, di_relopt_tab, lengthof(di_relopt_tab));
+
+	return (bytea *) rdopts;
+}
+
+/*
+ * Validator for index AM.
+ */
+static bool
+divalidate(Oid opclassoid)
+{
+	/* Index is dummy so we are happy with any opclass */
+	return true;
+}
+
+/*
+ * Begin scan of index AM.
+ */
+static IndexScanDesc
+dibeginscan(Relation r, int nkeys, int norderbys)
+{
+	IndexScanDesc scan;
+
+	/* Let's pretend we are doing something */
+	scan = RelationGetIndexScan(r, nkeys, norderbys);
+	return scan;
+}
+
+/*
+ * Rescan of index AM.
+ */
+static void
+direscan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+		 ScanKey orderbys, int norderbys)
+{
+	/* nothing to do */
+}
+
+/*
+ * End scan of index AM.
+ */
+static void
+diendscan(IndexScanDesc scan)
+{
+	/* nothing to do */
+}
+
+/*
+ * Index AM handler function: returns IndexAmRoutine with access method
+ * parameters and callbacks.
+ */
+Datum
+dihandler(PG_FUNCTION_ARGS)
+{
+	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+
+	amroutine->amstrategies = 0;
+	amroutine->amsupport = 1;
+	amroutine->amcanorder = false;
+	amroutine->amcanorderbyop = false;
+	amroutine->amcanbackward = false;
+	amroutine->amcanunique = false;
+	amroutine->amcanmulticol = false;
+	amroutine->amoptionalkey = false;
+	amroutine->amsearcharray = false;
+	amroutine->amsearchnulls = false;
+	amroutine->amstorage = false;
+	amroutine->amclusterable = false;
+	amroutine->ampredlocks = false;
+	amroutine->amcanparallel = false;
+	amroutine->amcaninclude = false;
+	amroutine->amkeytype = InvalidOid;
+
+	amroutine->ambuild = dibuild;
+	amroutine->ambuildempty = dibuildempty;
+	amroutine->aminsert = diinsert;
+	amroutine->ambulkdelete = dibulkdelete;
+	amroutine->amvacuumcleanup = divacuumcleanup;
+	amroutine->amcanreturn = NULL;
+	amroutine->amcostestimate = dicostestimate;
+	amroutine->amoptions = dioptions;
+	amroutine->amproperty = NULL;
+	amroutine->ambuildphasename = NULL;
+	amroutine->amvalidate = divalidate;
+	amroutine->ambeginscan = dibeginscan;
+	amroutine->amrescan = direscan;
+	amroutine->amgettuple = NULL;
+	amroutine->amgetbitmap = NULL;
+	amroutine->amendscan = diendscan;
+	amroutine->ammarkpos = NULL;
+	amroutine->amrestrpos = NULL;
+	amroutine->amestimateparallelscan = NULL;
+	amroutine->aminitparallelscan = NULL;
+	amroutine->amparallelrescan = NULL;
+
+	PG_RETURN_POINTER(amroutine);
+}
+
+void
+_PG_init(void)
+{
+	create_reloptions_table();
+}
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.control b/src/test/modules/dummy_index_am/dummy_index_am.control
new file mode 100644
index 0000000000..77bdea08ae
--- /dev/null
+++ b/src/test/modules/dummy_index_am/dummy_index_am.control
@@ -0,0 +1,5 @@
+# dummy_index_am extension
+comment = 'dummy_index_am - index access method template'
+default_version = '1.0'
+module_pathname = '$libdir/dummy_index_am'
+relocatable = true
diff --git a/src/test/modules/dummy_index_am/expected/reloptions.out b/src/test/modules/dummy_index_am/expected/reloptions.out
new file mode 100644
index 0000000000..04a120a0e0
--- /dev/null
+++ b/src/test/modules/dummy_index_am/expected/reloptions.out
@@ -0,0 +1,77 @@
+-- Tests for relation options
+CREATE EXTENSION dummy_index_am;
+CREATE TABLE dummy_test_tab (i int4);
+-- Test with default values.
+CREATE INDEX dummy_test_idx ON dummy_test_tab
+  USING dummy_index_am (i);
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
+ unnest 
+--------
+(0 rows)
+
+DROP INDEX dummy_test_idx;
+-- Test with full set of options.
+CREATE INDEX dummy_test_idx ON dummy_test_tab
+  USING dummy_index_am (i) WITH (
+  option_bool = false,
+  option_int = 5,
+  option_real = 3.1,
+  option_string_val = NULL,
+  option_string_null = 'val');
+NOTICE:  new option value for string parameter null
+NOTICE:  new option value for string parameter val
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
+         unnest         
+------------------------
+ option_bool=false
+ option_int=5
+ option_real=3.1
+ option_string_val=null
+ option_string_null=val
+(5 rows)
+
+-- ALTER INDEX .. SET
+ALTER INDEX dummy_test_idx SET (option_int = 10);
+NOTICE:  new option value for string parameter null
+NOTICE:  new option value for string parameter val
+ALTER INDEX dummy_test_idx SET (option_bool = true);
+NOTICE:  new option value for string parameter null
+NOTICE:  new option value for string parameter val
+ALTER INDEX dummy_test_idx SET (option_real = 3.2);
+NOTICE:  new option value for string parameter null
+NOTICE:  new option value for string parameter val
+ALTER INDEX dummy_test_idx SET (option_string_val = 'val2');
+NOTICE:  new option value for string parameter val
+NOTICE:  new option value for string parameter val2
+ALTER INDEX dummy_test_idx SET (option_string_null = NULL);
+NOTICE:  new option value for string parameter val2
+NOTICE:  new option value for string parameter null
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
+         unnest          
+-------------------------
+ option_int=10
+ option_bool=true
+ option_real=3.2
+ option_string_val=val2
+ option_string_null=null
+(5 rows)
+
+-- ALTER INDEX .. RESET
+ALTER INDEX dummy_test_idx RESET (option_int);
+NOTICE:  new option value for string parameter val2
+NOTICE:  new option value for string parameter null
+ALTER INDEX dummy_test_idx RESET (option_bool);
+NOTICE:  new option value for string parameter val2
+NOTICE:  new option value for string parameter null
+ALTER INDEX dummy_test_idx RESET (option_real);
+NOTICE:  new option value for string parameter val2
+NOTICE:  new option value for string parameter null
+ALTER INDEX dummy_test_idx RESET (option_string_val);
+NOTICE:  new option value for string parameter null
+ALTER INDEX dummy_test_idx RESET (option_string_null);
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
+ unnest 
+--------
+(0 rows)
+
+DROP INDEX dummy_test_idx;
diff --git a/src/test/modules/dummy_index_am/sql/reloptions.sql b/src/test/modules/dummy_index_am/sql/reloptions.sql
new file mode 100644
index 0000000000..10ca1a6d03
--- /dev/null
+++ b/src/test/modules/dummy_index_am/sql/reloptions.sql
@@ -0,0 +1,38 @@
+-- Tests for relation options
+CREATE EXTENSION dummy_index_am;
+
+CREATE TABLE dummy_test_tab (i int4);
+
+-- Test with default values.
+CREATE INDEX dummy_test_idx ON dummy_test_tab
+  USING dummy_index_am (i);
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
+DROP INDEX dummy_test_idx;
+
+-- Test with full set of options.
+CREATE INDEX dummy_test_idx ON dummy_test_tab
+  USING dummy_index_am (i) WITH (
+  option_bool = false,
+  option_int = 5,
+  option_real = 3.1,
+  option_string_val = NULL,
+  option_string_null = 'val');
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
+
+-- ALTER INDEX .. SET
+ALTER INDEX dummy_test_idx SET (option_int = 10);
+ALTER INDEX dummy_test_idx SET (option_bool = true);
+ALTER INDEX dummy_test_idx SET (option_real = 3.2);
+ALTER INDEX dummy_test_idx SET (option_string_val = 'val2');
+ALTER INDEX dummy_test_idx SET (option_string_null = NULL);
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
+
+-- ALTER INDEX .. RESET
+ALTER INDEX dummy_test_idx RESET (option_int);
+ALTER INDEX dummy_test_idx RESET (option_bool);
+ALTER INDEX dummy_test_idx RESET (option_real);
+ALTER INDEX dummy_test_idx RESET (option_string_val);
+ALTER INDEX dummy_test_idx RESET (option_string_null);
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
+
+DROP INDEX dummy_test_idx;
-- 
2.23.0

Attachment: signature.asc
Description: PGP signature

Reply via email to