diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 3d44616adc..e2063bac62 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -475,18 +475,18 @@ BloomInitMetapage(Relation index)
 bytea *
 bloptions(Datum reloptions, bool validate)
 {
-	relopt_value *options;
-	int			numoptions;
 	BloomOptions *rdopts;
 
 	/* Parse the user-given reloptions */
-	options = parseRelOptions(reloptions, validate, bl_relopt_kind, &numoptions);
-	rdopts = allocateReloptStruct(sizeof(BloomOptions), options, numoptions);
-	fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numoptions,
-				   validate, bl_relopt_tab, lengthof(bl_relopt_tab));
+	rdopts = (BloomOptions *) build_reloptions(reloptions, validate,
+											   bl_relopt_kind,
+											   sizeof(BloomOptions),
+											   bl_relopt_tab,
+											   lengthof(bl_relopt_tab));
 
 	/* Convert signature length from # of bits to # to words, rounding up */
-	rdopts->bloomLength = (rdopts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
+	if (rdopts)
+		rdopts->bloomLength = (rdopts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
 
 	return (bytea *) rdopts;
 }
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index ae7b729edd..b4f681a79e 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -820,29 +820,15 @@ brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
 bytea *
 brinoptions(Datum reloptions, bool validate)
 {
-	relopt_value *options;
-	BrinOptions *rdopts;
-	int			numoptions;
 	static const relopt_parse_elt tab[] = {
 		{"pages_per_range", RELOPT_TYPE_INT, offsetof(BrinOptions, pagesPerRange)},
 		{"autosummarize", RELOPT_TYPE_BOOL, offsetof(BrinOptions, autosummarize)}
 	};
 
-	options = parseRelOptions(reloptions, validate, RELOPT_KIND_BRIN,
-							  &numoptions);
-
-	/* if none set, we're done */
-	if (numoptions == 0)
-		return NULL;
-
-	rdopts = allocateReloptStruct(sizeof(BrinOptions), options, numoptions);
-
-	fillRelOptions((void *) rdopts, sizeof(BrinOptions), options, numoptions,
-				   validate, tab, lengthof(tab));
-
-	pfree(options);
-
-	return (bytea *) rdopts;
+	return (bytea *) build_reloptions(reloptions, validate,
+									  RELOPT_KIND_BRIN,
+									  sizeof(BrinOptions),
+									  tab, lengthof(tab));
 }
 
 /*
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index b5072c00fe..92f823b5c5 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -495,6 +495,14 @@ static bool need_initialization = true;
 static void initialize_reloptions(void);
 static void parse_one_reloption(relopt_value *option, char *text_str,
 								int text_len, bool validate);
+static relopt_value *parseRelOptions(Datum options, bool validate,
+									 relopt_kind kind, int *numrelopts);
+static void *allocateReloptStruct(Size base, relopt_value *options,
+								  int numoptions);
+static void fillRelOptions(void *rdopts, Size basesize,
+						   relopt_value *options, int numoptions,
+						   bool validate,
+						   const relopt_parse_elt *elems, int nelems);
 
 /*
  * initialize_reloptions
@@ -1122,6 +1130,73 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 }
 
 /*
+ * build_reloptions
+ *		Parses 'reloptions' provided by the caller in text-array format,
+ *		returning them in struct containing the parsed options
+ *
+ * Parsing is done with the help of a "parsing table" describing the allowed
+ * options, defined by 'relopt_elems' of length 'num_relopt_elems'.
+ *
+ * 'validate' must be true if reloptions value is freshly built by
+ * transformRelOptions(), as opposed to being read from the catalog, in which
+ * case the values contained in it must already be valid.
+ *
+ * NULL is returned if the passed in options didn't match any of the options
+ * in the "parsing table", unless validate is true in which case an error
+ * would be reported.
+ */
+void *
+build_reloptions(Datum reloptions, bool validate,
+				 relopt_kind kind,
+				 Size relopt_struct_size,
+				 const relopt_parse_elt *relopt_elems,
+				 int num_relopt_elems)
+{
+	int		numoptions;
+	relopt_value *options;
+	void   *rdopts;
+	/*
+	 * NB: this reloption type to size map relies on relopt_type enum not
+	 * changing.
+	 */
+	Size	relopt_type_size[] =
+				{sizeof(bool),
+				 sizeof(int),
+				 sizeof(double),
+				 sizeof(int),
+				 sizeof(char *)};
+
+	/* Parse options specific to given relopt_kind. */
+	options = parseRelOptions(reloptions, validate, kind, &numoptions);
+
+	/* If none set, we're done. */
+	if (numoptions == 0)
+	{
+		Assert(options == NULL);
+		return NULL;
+	}
+
+	/*
+	 * Allocate and fill the struct.
+	 *
+	 * The struct size and the relopt_parse_elt table that the caller passed
+	 * must correspond with each other, that is, all the values in the
+	 * relopt_elems must fit into rdopts.
+	 */
+	Assert(MAXALIGN(relopt_struct_size) ==
+		   MAXALIGN(relopt_elems[num_relopt_elems - 1].offset +
+					relopt_type_size[relopt_elems[num_relopt_elems - 1].opttype]));
+	rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
+	fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
+				   validate, relopt_elems, num_relopt_elems);
+
+	if (options)
+		pfree(options);
+
+	return rdopts;
+}
+
+/*
  * Interpret reloptions that are given in text-array format.
  *
  * options is a reloption text array as constructed by transformRelOptions.
@@ -1140,7 +1215,7 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
  * returned array.  Values of type string are allocated separately and must
  * be freed by the caller.
  */
-relopt_value *
+static relopt_value *
 parseRelOptions(Datum options, bool validate, relopt_kind kind,
 				int *numrelopts)
 {
@@ -1365,7 +1440,7 @@ parse_one_reloption(relopt_value *option, char *text_str, int text_len,
  * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
  * equivalent).
  */
-void *
+static void *
 allocateReloptStruct(Size base, relopt_value *options, int numoptions)
 {
 	Size		size = base;
@@ -1389,7 +1464,7 @@ allocateReloptStruct(Size base, relopt_value *options, int numoptions)
  * elems, of length numelems, is the table describing the allowed options.
  * When validate is true, it is expected that all options appear in elems.
  */
-void
+static void
 fillRelOptions(void *rdopts, Size basesize,
 			   relopt_value *options, int numoptions,
 			   bool validate,
@@ -1474,9 +1549,6 @@ fillRelOptions(void *rdopts, Size basesize,
 bytea *
 default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
 {
-	relopt_value *options;
-	StdRdOptions *rdopts;
-	int			numoptions;
 	static const relopt_parse_elt tab[] = {
 		{"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
 		{"autovacuum_enabled", RELOPT_TYPE_BOOL,
@@ -1521,20 +1593,9 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
 		offsetof(StdRdOptions, vacuum_truncate)}
 	};
 
-	options = parseRelOptions(reloptions, validate, kind, &numoptions);
-
-	/* if none set, we're done */
-	if (numoptions == 0)
-		return NULL;
-
-	rdopts = allocateReloptStruct(sizeof(StdRdOptions), options, numoptions);
-
-	fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numoptions,
-				   validate, tab, lengthof(tab));
-
-	pfree(options);
-
-	return (bytea *) rdopts;
+	return (bytea *) build_reloptions(reloptions, validate, kind,
+									  sizeof(StdRdOptions),
+									  tab, lengthof(tab));
 }
 
 /*
@@ -1543,9 +1604,6 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
 bytea *
 view_reloptions(Datum reloptions, bool validate)
 {
-	relopt_value *options;
-	ViewOptions *vopts;
-	int			numoptions;
 	static const relopt_parse_elt tab[] = {
 		{"security_barrier", RELOPT_TYPE_BOOL,
 		offsetof(ViewOptions, security_barrier)},
@@ -1553,20 +1611,10 @@ view_reloptions(Datum reloptions, bool validate)
 		offsetof(ViewOptions, check_option)}
 	};
 
-	options = parseRelOptions(reloptions, validate, RELOPT_KIND_VIEW, &numoptions);
-
-	/* if none set, we're done */
-	if (numoptions == 0)
-		return NULL;
-
-	vopts = allocateReloptStruct(sizeof(ViewOptions), options, numoptions);
-
-	fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numoptions,
-				   validate, tab, lengthof(tab));
-
-	pfree(options);
-
-	return (bytea *) vopts;
+	return (bytea *) build_reloptions(reloptions, validate,
+									  RELOPT_KIND_VIEW,
+									  sizeof(ViewOptions),
+									  tab, lengthof(tab));
 }
 
 /*
@@ -1628,29 +1676,15 @@ index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
 bytea *
 attribute_reloptions(Datum reloptions, bool validate)
 {
-	relopt_value *options;
-	AttributeOpts *aopts;
-	int			numoptions;
 	static const relopt_parse_elt tab[] = {
 		{"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
 		{"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
 	};
 
-	options = parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE,
-							  &numoptions);
-
-	/* if none set, we're done */
-	if (numoptions == 0)
-		return NULL;
-
-	aopts = allocateReloptStruct(sizeof(AttributeOpts), options, numoptions);
-
-	fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
-				   validate, tab, lengthof(tab));
-
-	pfree(options);
-
-	return (bytea *) aopts;
+	return (bytea *) build_reloptions(reloptions, validate,
+									  RELOPT_KIND_ATTRIBUTE,
+									  sizeof(AttributeOpts),
+									  tab, lengthof(tab));
 }
 
 /*
@@ -1659,30 +1693,16 @@ attribute_reloptions(Datum reloptions, bool validate)
 bytea *
 tablespace_reloptions(Datum reloptions, bool validate)
 {
-	relopt_value *options;
-	TableSpaceOpts *tsopts;
-	int			numoptions;
 	static const relopt_parse_elt tab[] = {
 		{"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
 		{"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)},
 		{"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, effective_io_concurrency)}
 	};
 
-	options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,
-							  &numoptions);
-
-	/* if none set, we're done */
-	if (numoptions == 0)
-		return NULL;
-
-	tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);
-
-	fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
-				   validate, tab, lengthof(tab));
-
-	pfree(options);
-
-	return (bytea *) tsopts;
+	return (bytea *) build_reloptions(reloptions, validate,
+									  RELOPT_KIND_TABLESPACE,
+									  sizeof(TableSpaceOpts),
+									  tab, lengthof(tab));
 }
 
 /*
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index cf9699ad18..38593554f0 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -602,30 +602,16 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
 bytea *
 ginoptions(Datum reloptions, bool validate)
 {
-	relopt_value *options;
-	GinOptions *rdopts;
-	int			numoptions;
 	static const relopt_parse_elt tab[] = {
 		{"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)},
 		{"gin_pending_list_limit", RELOPT_TYPE_INT, offsetof(GinOptions,
 															 pendingListCleanupSize)}
 	};
 
-	options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
-							  &numoptions);
-
-	/* if none set, we're done */
-	if (numoptions == 0)
-		return NULL;
-
-	rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);
-
-	fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numoptions,
-				   validate, tab, lengthof(tab));
-
-	pfree(options);
-
-	return (bytea *) rdopts;
+	return (bytea *) build_reloptions(reloptions, validate,
+									  RELOPT_KIND_GIN,
+									  sizeof(GinOptions),
+									  tab, lengthof(tab));
 }
 
 /*
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 45804d7a91..a23dec76a2 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -908,29 +908,15 @@ gistPageRecyclable(Page page)
 bytea *
 gistoptions(Datum reloptions, bool validate)
 {
-	relopt_value *options;
-	GiSTOptions *rdopts;
-	int			numoptions;
 	static const relopt_parse_elt tab[] = {
 		{"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
 		{"buffering", RELOPT_TYPE_ENUM, offsetof(GiSTOptions, buffering_mode)}
 	};
 
-	options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST,
-							  &numoptions);
-
-	/* if none set, we're done */
-	if (numoptions == 0)
-		return NULL;
-
-	rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);
-
-	fillRelOptions((void *) rdopts, sizeof(GiSTOptions), options, numoptions,
-				   validate, tab, lengthof(tab));
-
-	pfree(options);
-
-	return (bytea *) rdopts;
+	return (bytea *) build_reloptions(reloptions, validate,
+									  RELOPT_KIND_GIST,
+									  sizeof(GiSTOptions),
+									  tab, lengthof(tab));
 }
 
 /*
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 6bde2093d6..d4f6a168bd 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -25,7 +25,10 @@
 #include "nodes/pg_list.h"
 #include "storage/lock.h"
 
-/* types supported by reloptions */
+/*
+ * Types supported by reloptions.  NB: when changing this, see
+ * BuildRelOptions().
+ */
 typedef enum relopt_type
 {
 	RELOPT_TYPE_BOOL,
@@ -288,14 +291,12 @@ extern Datum transformRelOptions(Datum oldOptions, List *defList,
 extern List *untransformRelOptions(Datum options);
 extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 								amoptions_function amoptions);
-extern relopt_value *parseRelOptions(Datum options, bool validate,
-									 relopt_kind kind, int *numrelopts);
-extern void *allocateReloptStruct(Size base, relopt_value *options,
-								  int numoptions);
-extern void fillRelOptions(void *rdopts, Size basesize,
-						   relopt_value *options, int numoptions,
-						   bool validate,
-						   const relopt_parse_elt *elems, int nelems);
+
+extern void *build_reloptions(Datum reloptions, bool validate,
+				relopt_kind kind,
+				Size relopt_struct_size,
+				const relopt_parse_elt *relopt_elems,
+				int num_relopt_elems);
 
 extern bytea *default_reloptions(Datum reloptions, bool validate,
 								 relopt_kind kind);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index bc68767f3a..053636e4b4 100644
--- a/src/test/modules/dummy_index_am/dummy_index_am.c
+++ b/src/test/modules/dummy_index_am/dummy_index_am.c
@@ -222,17 +222,10 @@ dicostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 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;
+	return (bytea *) build_reloptions(reloptions, validate,
+									  di_relopt_kind,
+									  sizeof(DummyIndexOptions),
+									  di_relopt_tab, lengthof(di_relopt_tab));
 }
 
 /*
