diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index f3df1af..9c27326 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -204,7 +204,6 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
 			 void *callback_state);
 extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
 				IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
 extern void blcostestimate(PlannerInfo *root, IndexPath *path,
 			   double loop_count, Cost *indexStartupCost,
 			   Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index f2eda67..e937fed 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -35,49 +35,13 @@
 
 PG_FUNCTION_INFO_V1(blhandler);
 
-/* Kind of relation options for bloom index */
-static relopt_kind bl_relopt_kind;
-
-/* parse table for fillRelOptions */
-static relopt_parse_elt bl_relopt_tab[INDEX_MAX_KEYS + 1];
+/* Catalog of relation options for bloom index */
+static options_catalog *bl_relopt_catalog;
 
 static int32 myRand(void);
 static void mySrand(uint32 seed);
-
-/*
- * Module initialize function: initialize info about Bloom relation options.
- *
- * Note: keep this in sync with makeDefaultBloomOptions().
- */
-void
-_PG_init(void)
-{
-	int			i;
-	char		buf[16];
-
-	bl_relopt_kind = add_reloption_kind();
-
-	/* Option for length of signature */
-	add_int_reloption(bl_relopt_kind, "length",
-					  "Length of signature in bits",
-					  DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH);
-	bl_relopt_tab[0].optname = "length";
-	bl_relopt_tab[0].opttype = RELOPT_TYPE_INT;
-	bl_relopt_tab[0].offset = offsetof(BloomOptions, bloomLength);
-
-	/* Number of bits for each possible index column: col1, col2, ... */
-	for (i = 0; i < INDEX_MAX_KEYS; i++)
-	{
-		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);
-		bl_relopt_tab[i + 1].optname = MemoryContextStrdup(TopMemoryContext,
-														   buf);
-		bl_relopt_tab[i + 1].opttype = RELOPT_TYPE_INT;
-		bl_relopt_tab[i + 1].offset = offsetof(BloomOptions, bitSize[0]) + sizeof(int) * i;
-	}
-}
+static void *blrelopt_catalog(void);
+static void blReloptionPostprocess(void *, bool validate);
 
 /*
  * Construct a default set of Bloom options.
@@ -129,7 +93,7 @@ blhandler(PG_FUNCTION_ARGS)
 	amroutine->amvacuumcleanup = blvacuumcleanup;
 	amroutine->amcanreturn = NULL;
 	amroutine->amcostestimate = blcostestimate;
-	amroutine->amoptions = bloptions;
+	amroutine->amrelopt_catalog = blrelopt_catalog;
 	amroutine->amproperty = NULL;
 	amroutine->amvalidate = blvalidate;
 	amroutine->ambeginscan = blbeginscan;
@@ -146,6 +110,28 @@ blhandler(PG_FUNCTION_ARGS)
 	PG_RETURN_POINTER(amroutine);
 }
 
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+	BloomOptions *opts = (BloomOptions *) data;
+	int			i;
+
+	if (validate)
+		for (i = 0; i < INDEX_MAX_KEYS; i++)
+		{
+			if (opts->bitSize[i] >= opts->bloomLength)
+			{
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					   errmsg("col%i should not be grater than length", i)));
+			}
+		}
+
+	/* Convert signature length from # of bits to # to words, rounding up */
+	opts->bloomLength = (opts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
+}
+
+
 /*
  * Fill BloomState structure for particular index.
  */
@@ -466,24 +452,40 @@ BloomInitMetapage(Relation index)
 	UnlockReleaseBuffer(metaBuffer);
 }
 
-/*
- * Parse reloptions for bloom index, producing a BloomOptions struct.
- */
-bytea *
-bloptions(Datum reloptions, bool validate)
+static void *
+blrelopt_catalog(void)
 {
-	relopt_value *options;
-	int			numoptions;
-	BloomOptions *rdopts;
+	if (!bl_relopt_catalog)
+	{
 
-	/* 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));
+		int			i;
+		char		buf[16];
 
-	/* Convert signature length from # of bits to # to words, rounding up */
-	rdopts->bloomLength = (rdopts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
+		bl_relopt_catalog = allocateOptionsCatalog(NULL,
+								   sizeof(BloomOptions), INDEX_MAX_KEYS + 1);
+		bl_relopt_catalog->postprocess_fun = blReloptionPostprocess;
+
+		optionsCatalogAddItemInt(bl_relopt_catalog, "length",
+								 "Length of signature in bits",
+								 NoLock,		/* No lock as far as ALTER is
+												 * forbidden */
+								 OPTION_DEFINITION_FLAG_FORBID_ALTER,
+								 offsetof(BloomOptions, bloomLength),
+								 DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH);
 
-	return (bytea *) rdopts;
+		/* Number of bits for each possible index column: col1, col2, ... */
+		for (i = 0; i < INDEX_MAX_KEYS; i++)
+		{
+			snprintf(buf, 16, "col%d", i + 1);
+			optionsCatalogAddItemInt(bl_relopt_catalog, buf,
+								   "Number of bits for corresponding column",
+									 NoLock,	/* No lock as far as ALTER is
+												 * forbidden */
+									 OPTION_DEFINITION_FLAG_FORBID_ALTER,
+									 offsetof(BloomOptions, bitSize[i]),
+									 DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS);
+		}
+
+	}
+	return (void *) bl_relopt_catalog;
 }
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
index 5ab9e34..732f8b5 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -222,8 +222,27 @@ SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
 (1 row)
 
 -- check for min and max values
-\set VERBOSITY terse
 CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
 ERROR:  value 0 out of bounds for option "length"
+DETAIL:  Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+ERROR:  value 4097 out of bounds for option "length"
+DETAIL:  Valid values are between "1" and "4096".
 CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
 ERROR:  value 0 out of bounds for option "col1"
+DETAIL:  Valid values are between "1" and "4095".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+ERROR:  value 4096 out of bounds for option "col1"
+DETAIL:  Valid values are between "1" and "4095".
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+ERROR:  col0 should not be grater than length
+-- check that ALTERing of reloptions is forbidden
+ALTER INDEX bloomidx SET (length=4);
+ERROR:  changing parameter "length" is not allowed
+ALTER INDEX bloomidx SET (col1=4);
+ERROR:  changing parameter "col1" is not allowed
+ALTER INDEX bloomidx RESET (length);
+ERROR:  changing parameter "length" is not allowed
+ALTER INDEX bloomidx RESET (col1);
+ERROR:  changing parameter "col1" is not allowed
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 32755f2..f164e10 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -89,6 +89,16 @@ DROP INDEX bloomidx;
 CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1=4);
 SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
 -- check for min and max values
-\set VERBOSITY terse
 CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
 CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+
+-- check that ALTERing of reloptions is forbidden
+
+ALTER INDEX bloomidx SET (length=4);
+ALTER INDEX bloomidx SET (col1=4);
+ALTER INDEX bloomidx RESET (length);
+ALTER INDEX bloomidx RESET (col1);
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index a7e22b3..3cbef64 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1960,7 +1960,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
 Datum
 dblink_fdw_validator(PG_FUNCTION_ARGS)
 {
-	List	   *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+	List	   *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
 	Oid			context = PG_GETARG_OID(1);
 	ListCell   *cell;
 
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 370cc36..8efbcad 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -193,7 +193,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
 Datum
 file_fdw_validator(PG_FUNCTION_ARGS)
 {
-	List	   *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+	List	   *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
 	Oid			catalog = PG_GETARG_OID(1);
 	char	   *filename = NULL;
 	DefElem    *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 67e1c59..3fd44bc 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -65,7 +65,7 @@ PG_FUNCTION_INFO_V1(postgres_fdw_validator);
 Datum
 postgres_fdw_validator(PG_FUNCTION_ARGS)
 {
-	List	   *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+	List	   *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
 	Oid			catalog = PG_GETARG_OID(1);
 	ListCell   *cell;
 
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index cafc8fe..55b81be 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -19,7 +19,7 @@
 #include "access/brin_page.h"
 #include "access/brin_pageops.h"
 #include "access/brin_xlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
 #include "access/relscan.h"
 #include "access/xloginsert.h"
 #include "catalog/index.h"
@@ -73,6 +73,7 @@ static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a,
 			 BrinTuple *b);
 static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy);
 
+static void *bringetreloptcatalog(void);
 
 /*
  * BRIN handler function: return IndexAmRoutine with access method parameters
@@ -106,7 +107,6 @@ brinhandler(PG_FUNCTION_ARGS)
 	amroutine->amvacuumcleanup = brinvacuumcleanup;
 	amroutine->amcanreturn = NULL;
 	amroutine->amcostestimate = brincostestimate;
-	amroutine->amoptions = brinoptions;
 	amroutine->amproperty = NULL;
 	amroutine->amvalidate = brinvalidate;
 	amroutine->ambeginscan = brinbeginscan;
@@ -116,6 +116,7 @@ brinhandler(PG_FUNCTION_ARGS)
 	amroutine->amendscan = brinendscan;
 	amroutine->ammarkpos = NULL;
 	amroutine->amrestrpos = NULL;
+	amroutine->amrelopt_catalog = bringetreloptcatalog;
 	amroutine->amestimateparallelscan = NULL;
 	amroutine->aminitparallelscan = NULL;
 	amroutine->amparallelrescan = NULL;
@@ -800,37 +801,6 @@ brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
 }
 
 /*
- * reloptions processor for BRIN indexes
- */
-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;
-}
-
-/*
  * SQL-callable function to scan through an index and summarize all ranges
  * that are not currently summarized.
  */
@@ -1463,3 +1433,33 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy)
 	if (vacuum_fsm)
 		FreeSpaceMapVacuum(idxrel);
 }
+
+static options_catalog *brin_relopt_catalog = NULL;
+
+static void *
+bringetreloptcatalog(void)
+{
+	if (!brin_relopt_catalog)
+	{
+		brin_relopt_catalog = allocateOptionsCatalog(NULL,
+													 sizeof(BrinOptions), 2);
+
+		optionsCatalogAddItemInt(brin_relopt_catalog, "pages_per_range",
+			   "Number of pages that each page range covers in a BRIN index",
+								 NoLock,		/* since ALTER is not allowed
+												 * no lock needed */
+								 OPTION_DEFINITION_FLAG_FORBID_ALTER,
+								 offsetof(BrinOptions, pagesPerRange),
+								 BRIN_DEFAULT_PAGES_PER_RANGE,
+								 BRIN_MIN_PAGES_PER_RANGE,
+								 BRIN_MAX_PAGES_PER_RANGE);
+
+		optionsCatalogAddItemBool(brin_relopt_catalog, "autosummarize",
+						"Enables automatic summarization on this BRIN index",
+								  AccessExclusiveLock,
+								  0,
+								  offsetof(BrinOptions, autosummarize),
+								  false);
+	}
+	return brin_relopt_catalog;
+}
diff --git a/src/backend/access/common/Makefile b/src/backend/access/common/Makefile
index f130b6e..ef305e6 100644
--- a/src/backend/access/common/Makefile
+++ b/src/backend/access/common/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/common
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = bufmask.o heaptuple.o indextuple.o printsimple.o printtup.o \
+OBJS = bufmask.o heaptuple.o indextuple.o options.o printsimple.o printtup.o \
 	reloptions.o scankey.o session.o tupconvert.o tupdesc.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 0000000..6003e76
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1445 @@
+/*-------------------------------------------------------------------------
+ *
+ * options.c
+ *	  Core support for options used for relotions (pg_class.reloptions),
+ *	  attoptions (pg_attribute.attoptions), and can be used for other
+ *	  kinds of options.
+ *
+ *	  Here you can find functions that allow to define available options
+ *	  for certain object (certain retaion type, attribute type etc), and
+ *	  functions that allows to convert options from one represenation to
+ *	  another and validate them.
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/common/options.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/options.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "nodes/makefuncs.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+#include "mb/pg_wchar.h"
+
+/*
+ * Any object (i.e. relation type) that want to use options, should create an
+ * options catalog using  allocateOptionsCatalog function, fill it with
+ * optionsCatalogAddItemXxxx functions with definition of available options.
+ * Then instance of this object can take option values from syntax analyzer
+ * when instance in created using SQL command or from attribute of
+ * pg_catalog.yyyy table, when object that already exists is loaded form
+ * pg_catalog.
+ *
+ * Now options are mainly used for all kind or relations (pg_class.reloption)
+ * and also for attributes (pg_attribute.attoptions) and tablespaces
+ * (pg_tablespace.spcoptions)
+ *
+ * Options catalog for index relation options should be available from
+ * amrelopt_catalog access method function. For non-index relations and for
+ * other objects catalogs are available via functions from this file. E.g.
+ * get_heap_relopt_catalog for heap relation options catalog.
+ *
+ * This file has two sections:
+ *
+ * 1. Functions for managing option catalogs
+ *
+ * 2. Functions for manipulating and transforming options
+ *
+ * More explanations are available at the beginning of each section
+ */
+
+
+static option_definition_basic *allocateOptionDefinition(int type, const char *name,
+						 const char *desc, LOCKMODE lockmode,
+						 option_definition_flags flags, int struct_offset);
+
+static void parse_one_option(option_value * option, const char *text_str,
+				 int text_len, bool validate);
+static void *optionsAllocateBytea(options_catalog * catalog, List *options);
+
+
+static List *
+optionsDefListToRawValues(List *defList, options_parse_mode
+						  parse_mode);
+static Datum optionsValuesToTextArray(List *options_values);
+static List *optionsTextArrayToRawValues(Datum array_datum);
+static List *optionsMergeOptionValues(List *old_options, List *new_options);
+static List *optionsParseRawValues(List *raw_values, options_catalog * catalog,
+					  options_parse_mode mode);
+static bytea *optionsValuesToBytea(List *options, options_catalog * catalog);
+
+
+/*
+ * Options catalog functions
+ */
+
+/*
+ * Options catalog describes options available for certain object. Catalog has
+ * all necessary information for parsing transforming and validating options
+ * for an object. All parsing/validation/transformation functions should not
+ * know any details of option implementation for certain object, all this
+ * information should be stored in catalog instead and interpreted by
+ * pars/valid/transf functions blindly.
+ *
+ * The heart of the option catalog is an array of option definitions.  Options
+ * definition specifies name of option, type, range of acceptable values, and
+ * default value.
+ *
+ * Options values can be one of the following types: bool, int, real, enum,
+ * string. For more info see "option_type" and "optionsCatalogAddItemYyyy"
+ * functions.
+ *
+ * Option definition flags allows to define parser behavior for special (or not
+ * so special) cases. See option_definition_flags for more info.
+ *
+ * Options and Lock levels:
+ *
+ * The default choice for any new option should be AccessExclusiveLock.
+ * In some cases the lock level can be reduced from there, but the lock
+ * level chosen should always conflict with itself to ensure that multiple
+ * changes aren't lost when we attempt concurrent changes.
+ * The choice of lock level depends completely upon how that parameter
+ * is used within the server, not upon how and when you'd like to change it.
+ * Safety first. Existing choices are documented here, and elsewhere in
+ * backend code where the parameters are used.
+ *
+ * In general, anything that affects the results obtained from a SELECT must be
+ * protected by AccessExclusiveLock.
+ *
+ * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
+ * since they are only used by the AV procs and don't change anything
+ * currently executing.
+ *
+ * Fillfactor can be set because it applies only to subsequent changes made to
+ * data blocks, as documented in heapio.c
+ *
+ * n_distinct options can be set at ShareUpdateExclusiveLock because they
+ * are only used during ANALYZE, which uses a ShareUpdateExclusiveLock,
+ * so the ANALYZE will not be affected by in-flight changes. Changing those
+ * values has no affect until the next ANALYZE, so no need for stronger lock.
+ *
+ * Planner-related parameters can be set with ShareUpdateExclusiveLock because
+ * they only affect planning and not the correctness of the execution. Plans
+ * cannot be changed in mid-flight, so changes here could not easily result in
+ * new improved plans in any case. So we allow existing queries to continue
+ * and existing plans to survive, a small price to pay for allowing better
+ * plans to be introduced concurrently without interfering with users.
+ *
+ * Setting parallel_workers is safe, since it acts the same as
+ * max_parallel_workers_per_gather which is a USERSET parameter that doesn't
+ * affect existing plans or queries.
+*/
+
+/*
+ * allocateOptionsCatalog
+ *		Allocate memory for a new catalog and initializes structure members.
+ *
+ * namespace - name of a namespace in which all items of the catalog
+ * exists (E.g. namespace.option=value). For now postgres uses only toast.
+ * namespace for tables.
+ *
+ * size_of_bytea - size of bytea-packed C-structure where all option values
+ * would be finally mapped, so it can be fast and easily used by pg core
+ *
+ * num_items_expected - expected number of items in options catalog. If it is
+ * set to positive value, proper chunk of memory would be allocated, and postgres
+ * will assert if you try to add more options to the catalog. If it is set to -1, then
+ * you can add any number of options to catalog, memory for entries would be dynamically
+ * reallocated while adding. This feature was left for backward compatibility and is
+ * not encouraged for production usage.
+ */
+
+options_catalog *
+allocateOptionsCatalog(const char *namespace, int size_of_bytea, int num_items_expected)
+{
+	MemoryContext oldcxt;
+	options_catalog *catalog;
+
+	oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+	catalog = palloc(sizeof(options_catalog));
+	if (namespace)
+	{
+		catalog->namespace = palloc(strlen(namespace) + 1);
+		strcpy(catalog->namespace, namespace);
+	}
+	else
+		catalog->namespace = NULL;
+	if (num_items_expected > 0)
+	{
+		catalog->num_allocated = num_items_expected;
+		catalog->forbid_realloc = true;
+		catalog->definitions = palloc(
+				 catalog->num_allocated * sizeof(option_definition_basic *));
+	}
+	else
+	{
+		catalog->num_allocated = 0;
+		catalog->forbid_realloc = false;
+		catalog->definitions = NULL;
+	}
+	catalog->num = 0;
+	catalog->struct_size = size_of_bytea;
+	catalog->postprocess_fun = NULL;
+	MemoryContextSwitchTo(oldcxt);
+	return catalog;
+}
+
+/*
+ * optionCatalogAddItem
+ *		Add an already-created option definition to the catalog
+ */
+static void
+optionCatalogAddItem(option_definition_basic * newoption,
+					 options_catalog * catalog)
+{
+	if (catalog->num >= catalog->num_allocated)
+	{
+		MemoryContext oldcxt;
+
+		Assert(!catalog->forbid_realloc);
+		oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+
+		if (catalog->num_allocated == 0)
+		{
+			catalog->num_allocated = 8;
+			catalog->definitions = palloc(
+				 catalog->num_allocated * sizeof(option_definition_basic *));
+		}
+		else
+		{
+			catalog->num_allocated *= 2;
+			catalog->definitions = repalloc(catalog->definitions,
+				 catalog->num_allocated * sizeof(option_definition_basic *));
+		}
+		MemoryContextSwitchTo(oldcxt);
+	}
+	catalog->definitions[catalog->num] = newoption;
+	catalog->num++;
+}
+
+/*
+ * allocateOptionDefinition
+ *		Allocate a new option definition and initialize the type-agnostic
+ *		fields
+ */
+static option_definition_basic *
+allocateOptionDefinition(int type, const char *name, const char *desc, LOCKMODE lockmode,
+						 option_definition_flags flags, int struct_offset)
+{
+	MemoryContext oldcxt;
+	size_t		size;
+	option_definition_basic *newoption;
+
+	oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+
+	switch (type)
+	{
+		case OPTION_TYPE_BOOL:
+			size = sizeof(option_definition_bool);
+			break;
+		case OPTION_TYPE_INT:
+			size = sizeof(option_definition_int);
+			break;
+		case OPTION_TYPE_REAL:
+			size = sizeof(option_definition_real);
+			break;
+		case OPTION_TYPE_ENUM:
+			size = sizeof(option_definition_enum);
+			break;
+		case OPTION_TYPE_STRING:
+			size = sizeof(option_definition_string);
+			break;
+		default:
+			elog(ERROR, "unsupported reloption type %d", type);
+			return NULL;		/* keep compiler quiet */
+	}
+
+	newoption = palloc(size);
+
+	newoption->name = pstrdup(name);
+	if (desc)
+		newoption->desc = pstrdup(desc);
+	else
+		newoption->desc = NULL;
+	newoption->type = type;
+	newoption->lockmode = lockmode;
+	newoption->flags = flags;
+	newoption->struct_offset = struct_offset;
+
+	MemoryContextSwitchTo(oldcxt);
+
+	return newoption;
+}
+
+/*
+ * optionsCatalogAddItemBool
+ *		Add a new boolean option definition to the catalog
+ */
+void
+optionsCatalogAddItemBool(options_catalog * catalog, const char *name, const char *desc,
+						  LOCKMODE lockmode, option_definition_flags flags,
+						  int struct_offset, bool default_val)
+{
+	option_definition_bool *catalog_item;
+
+	catalog_item = (option_definition_bool *)
+		allocateOptionDefinition(OPTION_TYPE_BOOL, name, desc, lockmode,
+								 flags, struct_offset);
+
+	catalog_item->default_val = default_val;
+
+	optionCatalogAddItem((option_definition_basic *) catalog_item, catalog);
+}
+
+/*
+ * optionsCatalogAddItemInt
+ *		Add a new integer option definition to the catalog
+ */
+void
+optionsCatalogAddItemInt(options_catalog * catalog, const char *name,
+		  const char *desc, LOCKMODE lockmode, option_definition_flags flags,
+				int struct_offset, int default_val, int min_val, int max_val)
+{
+	option_definition_int *catalog_item;
+
+	catalog_item = (option_definition_int *)
+		allocateOptionDefinition(OPTION_TYPE_INT, name, desc, lockmode,
+								 flags, struct_offset);
+
+	catalog_item->default_val = default_val;
+	catalog_item->min = min_val;
+	catalog_item->max = max_val;
+
+	optionCatalogAddItem((option_definition_basic *) catalog_item, catalog);
+}
+
+/*
+ * optionsCatalogAddItemReal
+ *		Add a new float option to the catalog
+ */
+void
+optionsCatalogAddItemReal(options_catalog * catalog, const char *name, const char *desc,
+		 LOCKMODE lockmode, option_definition_flags flags, int struct_offset,
+						  double default_val, double min_val, double max_val)
+{
+	option_definition_real *catalog_item;
+
+	catalog_item = (option_definition_real *)
+		allocateOptionDefinition(OPTION_TYPE_REAL, name, desc, lockmode,
+								 flags, struct_offset);
+
+	catalog_item->default_val = default_val;
+	catalog_item->min = min_val;
+	catalog_item->max = max_val;
+
+	optionCatalogAddItem((option_definition_basic *) catalog_item, catalog);
+}
+
+/*
+ * optionsCatalogAddItemEnum
+ *		Add a new enum option to the catalog
+ *
+ * "allowed_values" is a pointer to a NULL-terminated char* array (last item of
+ * an array should be null). This array contains a list of acceptable values
+ * for the option.
+ *
+ * "default_val" is a number of item in allowed_values array that should be
+ * set as default value. If you want to handle "option was not set" special
+ * case, you can set default_val to -1
+ */
+void
+optionsCatalogAddItemEnum(options_catalog * catalog, const char *name, const char *desc,
+		 LOCKMODE lockmode, option_definition_flags flags, int struct_offset,
+						  const char **allowed_values, int default_val)
+{
+	option_definition_enum *catalog_item;
+
+	catalog_item = (option_definition_enum *)
+		allocateOptionDefinition(OPTION_TYPE_ENUM, name, desc, lockmode,
+								 flags, struct_offset);
+
+	catalog_item->default_val = default_val;
+	catalog_item->allowed_values = allowed_values;
+
+	optionCatalogAddItem((option_definition_basic *) catalog_item, catalog);
+}
+
+/*
+ * optionsCatalogAddItemString
+ *		Add a new string option definition to the catalog
+ *
+ * "validator" is an optional function pointer that can be used to test the
+ * validity of the values.  It must elog(ERROR) when the argument string is
+ * not acceptable for the variable.  Note that the default value must pass
+ * the validation.
+ */
+void
+optionsCatalogAddItemString(options_catalog * catalog, const char *name, const char *desc,
+		 LOCKMODE lockmode, option_definition_flags flags, int struct_offset,
+				   const char *default_val, validate_string_option validator)
+{
+	option_definition_string *catalog_item;
+
+	/* make sure the validator/default combination is sane */
+	if (validator)
+		(validator) (default_val);
+
+	catalog_item = (option_definition_string *)
+		allocateOptionDefinition(OPTION_TYPE_STRING, name, desc, lockmode,
+								 flags, struct_offset);
+	catalog_item->validate_cb = validator;
+
+	if (default_val)
+		catalog_item->default_val = MemoryContextStrdup(TopMemoryContext,
+														default_val);
+	else
+		catalog_item->default_val = NULL;
+	optionCatalogAddItem((option_definition_basic *) catalog_item, catalog);
+}
+
+
+/*
+ * Options transform functions
+ */
+
+/*
+ * Option values exists in five representations: DefList, TextArray, Values and
+ * Bytea:
+ *
+ * DefList: Is a List of DefElem structures, that comes from syntax analyzer.
+ * It can be transformed to Values representation for further parsing and
+ * validating
+ *
+ * Values: A List of option_value structures. Is divided into two subclasses:
+ * RawValues, when values are already transformed from DefList or TextArray,
+ * but not parsed yet. (In this case you should use raw_name and raw_value
+ * structure members to see option content). ParsedValues (or just simple
+ * Values) is crated after finding a definition for this option in a catalog
+ * and after parsing of the raw value. For ParsedValues content is stored in
+ * values structure member, and name can be taken from option definition in gen
+ * structure member.  Actually Value list can have both Raw and Parsed values,
+ * as we do not validate options that came from database, and db option that
+ * does not exist in catalog is just ignored, and kept as RawValues
+ *
+ * TextArray: The representation in which  options for existing object comes
+ * and goes from/to database; for example from pg_class.reloptions. It is a
+ * plain TEXT[] db object with name=value text inside. This representation can
+ * be transformed into Values for further processing, using options catalog.
+ *
+ * Bytea: Is a binary representation of options. Each object that has code that
+ * uses options, should create a C-structure for this options, with varlen
+ * 4-byte header in front of the data; all items of options catalog should have
+ * an offset of a corresponding binary data in this structure, so transform
+ * function can put this data in the correct place. One can transform options
+ * data from values representation into Bytea, using catalog data, and then use
+ * it as a usual Datum object, when needed. This Datum should be cached
+ * somewhere (for example in rel->rd_options for relations) when object that
+ * has option is loaded from db.
+ */
+
+
+/* optionsDefListToRawValues
+ *		Converts option values that came from syntax analyzer (DefList) into
+ *		Values List.
+ *
+ * No parsing is done here except for checking that RESET syntax is correct
+ * (syntax analyzer do not see difference between SET and RESET cases, we
+ * should treat it here manually
+ */
+static List *
+optionsDefListToRawValues(List *defList, options_parse_mode parse_mode)
+{
+	ListCell   *cell;
+	List	   *result = NIL;
+
+	foreach(cell, defList)
+	{
+		option_value *option_dst;
+		DefElem    *def = (DefElem *) lfirst(cell);
+		char	   *value;
+
+		option_dst = palloc(sizeof(option_value));
+
+		if (def->defnamespace)
+		{
+			option_dst->namespace = palloc(strlen(def->defnamespace) + 1);
+			strcpy(option_dst->namespace, def->defnamespace);
+		}
+		else
+		{
+			option_dst->namespace = NULL;
+		}
+		option_dst->raw_name = palloc(strlen(def->defname) + 1);
+		strcpy(option_dst->raw_name, def->defname);
+
+		if (parse_mode & OPTIONS_PARSE_MODE_FOR_RESET)
+		{
+			/*
+			 * If this option came from RESET statement we should throw error
+			 * it it brings us name=value data, as syntax analyzer do not
+			 * prevent it
+			 */
+			if (def->arg != NULL)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+					errmsg("RESET must not include values for parameters")));
+
+			option_dst->status = OPTION_VALUE_STATUS_FOR_RESET;
+		}
+		else
+		{
+			/*
+			 * For SET statement we should treat (name) expression as if it is
+			 * actually (name=true) so do it here manually. In other cases
+			 * just use value as we should use it
+			 */
+			option_dst->status = OPTION_VALUE_STATUS_RAW;
+			if (def->arg != NULL)
+				value = defGetString(def);
+			else
+				value = "true";
+			option_dst->raw_value = palloc(strlen(value) + 1);
+			strcpy(option_dst->raw_value, value);
+		}
+
+		result = lappend(result, option_dst);
+	}
+	return result;
+}
+
+/*
+ * optionsValuesToTextArray
+ *		Converts List of option_values into TextArray
+ *
+ *	Convertation is made to put options into database (e.g. in
+ *	pg_class.reloptions for all relation options)
+ */
+
+Datum
+optionsValuesToTextArray(List *options_values)
+{
+	ArrayBuildState *astate = NULL;
+	ListCell   *cell;
+	Datum		result;
+
+	foreach(cell, options_values)
+	{
+		option_value *option = (option_value *) lfirst(cell);
+		const char *name;
+		char	   *value;
+		text	   *t;
+		int			len;
+
+		/*
+		 * Raw value were not cleared while parsing, so instead of converting
+		 * it back, just use it to store value as text
+		 */
+		value = option->raw_value;
+
+		Assert(option->status != OPTION_VALUE_STATUS_EMPTY);
+
+		/*
+		 * Name will be taken from option definition, if option were parsed or
+		 * from raw_name if option were not parsed for some reason
+		 */
+		if (option->status == OPTION_VALUE_STATUS_PARSED)
+			name = option->gen->name;
+		else
+			name = option->raw_name;
+
+		/*
+		 * Now build "name=value" string and append it to the array
+		 */
+		len = VARHDRSZ + strlen(name) + strlen(value) + 1;
+		t = (text *) palloc(len + 1);
+		SET_VARSIZE(t, len);
+		sprintf(VARDATA(t), "%s=%s", name, value);
+		astate = accumArrayResult(astate, PointerGetDatum(t), false,
+								  TEXTOID, CurrentMemoryContext);
+	}
+	if (astate)
+		result = makeArrayResult(astate, CurrentMemoryContext);
+	else
+		result = (Datum) 0;
+
+	return result;
+}
+
+/*
+ * optionsTextArrayToRawValues
+ *		Converts options from TextArray format into RawValues list.
+ *
+ *	This function is used to convert options data that comes from database to
+ *	List of option_values, for further parsing, and, in the case of ALTER
+ *	command, for merging with new option values.
+ */
+static List *
+optionsTextArrayToRawValues(Datum array_datum)
+{
+	List	   *result = NIL;
+
+	if (PointerIsValid(DatumGetPointer(array_datum)))
+	{
+		ArrayType  *array = DatumGetArrayTypeP(array_datum);
+		Datum	   *options;
+		int			noptions;
+		int			i;
+
+		deconstruct_array(array, TEXTOID, -1, false, 'i',
+						  &options, NULL, &noptions);
+
+		for (i = 0; i < noptions; i++)
+		{
+			option_value *option_dst;
+			char	   *text_str = VARDATA(options[i]);
+			int			text_len = VARSIZE(options[i]) - VARHDRSZ;
+			int			i;
+			int			name_len = -1;
+			char	   *name;
+			int			raw_value_len;
+			char	   *raw_value;
+
+			/*
+			 * Find position of '=' sign and treat id as a separator between
+			 * name and value in "name=value" item
+			 */
+			for (i = 0; i < text_len; i = i + pg_mblen(text_str))
+			{
+				if (text_str[i] == '=')
+				{
+					name_len = i;
+					break;
+				}
+			}
+			Assert(name_len >= 1);		/* Just in case */
+
+			raw_value_len = text_len - name_len - 1;
+
+			/*
+			 * Copy name from src
+			 */
+			name = palloc(name_len + 1);
+			memcpy(name, text_str, name_len);
+			name[name_len] = '\0';
+
+			/*
+			 * Copy value from src
+			 */
+			raw_value = palloc(raw_value_len + 1);
+			memcpy(raw_value, text_str + name_len + 1, raw_value_len);
+			raw_value[raw_value_len] = '\0';
+
+			/*
+			 * Create new option_value item
+			 */
+			option_dst = palloc(sizeof(option_value));
+			option_dst->status = OPTION_VALUE_STATUS_RAW;
+			option_dst->raw_name = name;
+			option_dst->raw_value = raw_value;
+
+			result = lappend(result, option_dst);
+		}
+	}
+	return result;
+}
+
+/*
+ * optionsMergeOptionValues
+ *		Merges two lists of option_values into one list
+ *
+ * This function is used to merge two Values list into one. It is used for all
+ * kinds of ALTER commands when existing options are merged|replaced with new
+ * options list. This function also process RESET variant of ALTER command. It
+ * merges two lists as usual, and then removes all items with RESET flag on.
+ *
+ * Both incoming lists will be destroyed while merging
+ */
+static List *
+optionsMergeOptionValues(List *old_options, List *new_options)
+{
+	List	   *result = NIL;
+	ListCell   *old_cell;
+	ListCell   *old_prev;
+	ListCell   *old_next;
+	ListCell   *new_cell;
+	ListCell   *new_prev;
+	ListCell   *new_next;
+
+	old_prev = NULL;
+
+	/*
+	 * First try to remove from old options list all values that exists in a
+	 * new options list
+	 */
+	for (old_cell = list_head(old_options); old_cell; old_cell = old_next)
+	{
+		bool		found;
+		const char *old_name;
+		option_value *old_option;
+
+		old_next = lnext(old_cell);
+		old_option = (option_value *) lfirst(old_cell);
+		if (old_option->status == OPTION_VALUE_STATUS_PARSED)
+			old_name = old_option->gen->name;
+		else
+			old_name = old_option->raw_name;
+
+		/*
+		 * Try to find old_name option among new option
+		 */
+		found = false;
+		foreach(new_cell, new_options)
+		{
+			option_value *new_option;
+			const char *new_name;
+
+			new_option = (option_value *) lfirst(new_cell);
+			if (new_option->status == OPTION_VALUE_STATUS_PARSED)
+				new_name = new_option->gen->name;
+			else
+				new_name = new_option->raw_name;
+			if (pg_strcasecmp(new_name, old_name) == 0)
+			{
+				found = true;
+				break;
+			}
+		}
+
+		/*
+		 * If found, delete old option from the list
+		 */
+		if (found)
+		{
+			old_options = list_delete_cell(old_options, old_cell, old_prev);
+		}
+		else
+		{
+			old_prev = old_cell;
+		}
+	}
+
+	/*
+	 * Remove from new_options all options that are for RESET. In old list all
+	 * this options were already removed in previous block, so all RESET
+	 * options are cleaned now
+	 */
+	new_prev = NULL;
+	for (new_cell = list_head(new_options); new_cell; new_cell = new_next)
+	{
+		option_value *new_option = (option_value *) lfirst(new_cell);
+
+		new_next = lnext(new_cell);
+
+		if (new_option->status == OPTION_VALUE_STATUS_FOR_RESET)
+			new_options = list_delete_cell(new_options, new_cell, new_prev);
+		else
+			new_prev = new_cell;
+	}
+
+	/*
+	 * Now merge what remained of both lists
+	 */
+	result = list_concat(old_options, new_options);
+	return result;
+}
+
+/*
+ * optionsDefListValdateNamespaces
+ *		Function checks that all options represented as DefList has no
+ *		namespaces or have namespaces only from allowed list
+ *
+ * Function accept options as DefList and NULL terminated list of allowed
+ * namespaces. It throws an error if not proper namespace was found.
+ *
+ * This function actually used only for tables with it's toast. namespace
+ */
+void
+optionsDefListValdateNamespaces(List *defList, char **allowed_namespaces)
+{
+	ListCell   *cell;
+
+	foreach(cell, defList)
+	{
+		DefElem    *def = (DefElem *) lfirst(cell);
+
+		/*
+		 * Checking namespace only for options that have namespaces. Options
+		 * with no namespaces are always accepted
+		 */
+		if (def->defnamespace)
+		{
+			bool		found = false;
+			int			i = 0;
+
+			while (allowed_namespaces[i])
+			{
+				if (pg_strcasecmp(def->defnamespace,
+								  allowed_namespaces[i]) == 0)
+				{
+					found = true;
+					break;
+				}
+				i++;
+			}
+			if (!found)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("unrecognized parameter namespace \"%s\"",
+								def->defnamespace)));
+		}
+	}
+}
+
+/*
+ * optionsDefListFilterNamespaces
+ *		Iterates over DefList, choose items with specified namespace and adds
+ *		them to a result List
+ *
+ * This function does not destroy source DefList but does not create copies
+ * of List nodes.
+ * It is actually used only for tables, in order to split toast and heap
+ * reloptions, so each one can be stored in on it's own pg_class record
+ */
+List *
+optionsDefListFilterNamespaces(List *defList, const char *namespace)
+{
+	ListCell   *cell;
+	List	   *result = NIL;
+
+	foreach(cell, defList)
+	{
+		DefElem    *def = (DefElem *) lfirst(cell);
+
+		if ((!namespace && !def->defnamespace) ||
+			(namespace && def->defnamespace &&
+			 pg_strcasecmp(namespace, def->defnamespace) == 0))
+		{
+			result = lappend(result, def);
+		}
+	}
+	return result;
+}
+
+/*
+ * optionsTextArrayToDefList
+ *		Convert the text-array format of reloptions into a List of DefElem.
+ */
+List *
+optionsTextArrayToDefList(Datum options)
+{
+	List	   *result = NIL;
+	ArrayType  *array;
+	Datum	   *optiondatums;
+	int			noptions;
+	int			i;
+
+	/* Nothing to do if no options */
+	if (!PointerIsValid(DatumGetPointer(options)))
+		return result;
+
+	array = DatumGetArrayTypeP(options);
+
+	deconstruct_array(array, TEXTOID, -1, false, 'i',
+					  &optiondatums, NULL, &noptions);
+
+	for (i = 0; i < noptions; i++)
+	{
+		char	   *s;
+		char	   *p;
+		Node	   *val = NULL;
+
+		s = TextDatumGetCString(optiondatums[i]);
+		p = strchr(s, '=');
+		if (p)
+		{
+			*p++ = '\0';
+			val = (Node *) makeString(pstrdup(p));
+		}
+		result = lappend(result, makeDefElem(pstrdup(s), val, -1));
+	}
+
+	return result;
+}
+
+/*
+ * optionsParseRawValues
+ *		Parses and vlaidates (if proper flag is set) option_values. As a result
+ *		caller will get the list of parsed (or partly parsed) option_values
+ *
+ * This function is used in cases when caller gets raw values from db or
+ * syntax and want to parse them.
+ * This function uses option_catalog to get information about how each option
+ * should be parsed.
+ * If validate mode is off, function found an option that do not have proper
+ * option_catalog entry, this option kept unparsed (if some garbage came from
+ * the DB, we should put it back there)
+ *
+ * This function destroys incoming list.
+ */
+static List *
+optionsParseRawValues(List *raw_values, options_catalog * catalog,
+					  options_parse_mode mode)
+{
+	ListCell   *cell;
+	List	   *result = NIL;
+	bool	   *is_set;
+	int			i;
+	bool		validate = mode & OPTIONS_PARSE_MODE_VALIDATE;
+	bool		for_alter = mode & OPTIONS_PARSE_MODE_FOR_ALTER;
+
+
+	is_set = palloc0(sizeof(bool) * catalog->num);
+	foreach(cell, raw_values)
+	{
+		option_value *option = (option_value *) lfirst(cell);
+		bool		found = false;
+		bool		skip = false;
+
+
+		if (option->status == OPTION_VALUE_STATUS_PARSED)
+		{
+			/*
+			 * This can happen while ALTER, when new values were already
+			 * parsed, but old values merged from DB are still raw
+			 */
+			result = lappend(result, option);
+			continue;
+		}
+		if (validate && option->namespace && (!catalog->namespace ||
+				  pg_strcasecmp(catalog->namespace, option->namespace) != 0))
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unrecognized parameter namespace \"%s\"",
+							option->namespace)));
+		}
+
+		for (i = 0; i < catalog->num; i++)
+		{
+			option_definition_basic *definition = catalog->definitions[i];
+
+			if (pg_strcasecmp(option->raw_name,
+							  definition->name) == 0)
+			{
+				/*
+				 * Skip option with "ignore" flag, as it is processed
+				 * somewhere else. (WITH OIDS special case)
+				 */
+				if (definition->flags & OPTION_DEFINITION_FLAG_IGNORE)
+				{
+					found = true;
+					skip = true;
+					break;
+				}
+
+				/*
+				 * Reject option as if it was not in catalog. Needed for cases
+				 * when option should have default value, but should not be
+				 * changed
+				 */
+				if (definition->flags & OPTION_DEFINITION_FLAG_REJECT)
+				{
+					found = false;
+					break;
+				}
+
+				if (validate && is_set[i])
+				{
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						  errmsg("parameter \"%s\" specified more than once",
+								 option->raw_name)));
+				}
+				if ((for_alter) &&
+					(definition->flags & OPTION_DEFINITION_FLAG_FORBID_ALTER))
+				{
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						   errmsg("changing parameter \"%s\" is not allowed",
+								  definition->name)));
+				}
+				if (option->status == OPTION_VALUE_STATUS_FOR_RESET)
+				{
+					/*
+					 * For RESET options do not need further processing so
+					 * mark it found and stop searching
+					 */
+					found = true;
+					break;
+				}
+				pfree(option->raw_name);
+				option->raw_name = NULL;
+				option->gen = definition;
+				parse_one_option(option, NULL, -1, validate);
+				is_set[i] = true;
+				found = true;
+				break;
+			}
+		}
+		if (!found)
+		{
+			if (validate)
+			{
+				if (option->namespace)
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("unrecognized parameter \"%s.%s\"",
+									option->namespace, option->raw_name)));
+				else
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("unrecognized parameter \"%s\"",
+									option->raw_name)));
+			}
+
+			/*
+			 * If we are parsing not in validate mode, then we should keep
+			 * unknown node, because non-validate mode is for data that is
+			 * already in the DB and should not be changed after altering
+			 * another entries
+			 */
+		}
+		if (!skip)
+			result = lappend(result, option);
+	}
+	return result;
+}
+
+/*
+ * parse_one_option
+ *
+ *		Subroutine for optionsParseRawValues, to parse and validate a
+ *		single option's value
+ */
+static void
+parse_one_option(option_value * option, const char *text_str, int text_len,
+				 bool validate)
+{
+	char	   *value;
+	bool		parsed;
+
+	value = option->raw_value;
+
+	switch (option->gen->type)
+	{
+		case OPTION_TYPE_BOOL:
+			{
+				parsed = parse_bool(value, &option->values.bool_val);
+				if (validate && !parsed)
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("invalid value for boolean option \"%s\": %s",
+							   option->gen->name, value)));
+			}
+			break;
+		case OPTION_TYPE_INT:
+			{
+				option_definition_int *optint =
+				(option_definition_int *) option->gen;
+
+				parsed = parse_int(value, &option->values.int_val, 0, NULL);
+				if (validate && !parsed)
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("invalid value for integer option \"%s\": %s",
+							   option->gen->name, value)));
+				if (validate && (option->values.int_val < optint->min ||
+								 option->values.int_val > optint->max))
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						   errmsg("value %s out of bounds for option \"%s\"",
+								  value, option->gen->name),
+					 errdetail("Valid values are between \"%d\" and \"%d\".",
+							   optint->min, optint->max)));
+			}
+			break;
+		case OPTION_TYPE_REAL:
+			{
+				option_definition_real *optreal =
+				(option_definition_real *) option->gen;
+
+				parsed = parse_real(value, &option->values.real_val);
+				if (validate && !parsed)
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("invalid value for floating point option \"%s\": %s",
+									option->gen->name, value)));
+				if (validate && (option->values.real_val < optreal->min ||
+								 option->values.real_val > optreal->max))
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						   errmsg("value %s out of bounds for option \"%s\"",
+								  value, option->gen->name),
+					 errdetail("Valid values are between \"%f\" and \"%f\".",
+							   optreal->min, optreal->max)));
+			}
+			break;
+		case OPTION_TYPE_ENUM:
+			{
+				option_definition_enum *opt_enum =
+				(option_definition_enum *) option->gen;
+				int			i = 0;
+
+				parsed = false;
+				while (opt_enum->allowed_values[i])
+				{
+					if (pg_strcasecmp(value, opt_enum->allowed_values[i]) == 0)
+					{
+						option->values.enum_val = i;
+						parsed = true;
+						break;
+					}
+					i++;
+				}
+				if (!parsed)
+				{
+					int			length = 0;
+					char	   *str;
+					char	   *ptr;
+
+					/*
+					 * Generating list of allowed values: "value1", "value2",
+					 * ... "valueN"
+					 */
+					i = 0;
+					while (opt_enum->allowed_values[i])
+					{
+						length += strlen(opt_enum->allowed_values[i]) + 4;
+						/* +4: two quotes, one comma, one space */
+						i++;
+					}
+
+					/*
+					 * one byte not used for comma after the last item will be
+					 * used for \0; for another byte will do -1
+					 */
+					str = palloc((length - 1) * sizeof(char));
+					i = 0;
+					ptr = str;
+					while (opt_enum->allowed_values[i])
+					{
+						if (i != 0)
+						{
+							ptr[0] = ',';
+							ptr[1] = ' ';
+							ptr += 2;
+						}
+						ptr[0] = '"';
+						ptr++;
+						sprintf(ptr, "%s", opt_enum->allowed_values[i]);
+						ptr += strlen(ptr);
+						ptr[0] = '"';
+						ptr++;
+						i++;
+					}
+					*ptr = '\0';
+
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("invalid value for \"%s\" option",
+									option->gen->name),
+							 errdetail("Valid values are %s.", str)));
+				}
+			}
+			break;
+		case OPTION_TYPE_STRING:
+			{
+				option_definition_string *optstring =
+				(option_definition_string *) option->gen;
+
+				option->values.string_val = value;
+				if (validate && optstring->validate_cb)
+					(optstring->validate_cb) (value);
+				parsed = true;
+			}
+			break;
+		default:
+			elog(ERROR, "unsupported reloption type %d", option->gen->type);
+			parsed = true;		/* quiet compiler */
+			break;
+	}
+
+	if (parsed)
+		option->status = OPTION_VALUE_STATUS_PARSED;
+
+}
+
+/*
+ * optionsAllocateBytea
+ *		Allocates memory for bytea options representation
+ *
+ * Function allocates memory for byrea structure of an option, plus adds space
+ * for values of string options. We should keep all data including string
+ * values in the same memory chunk, because Cache code copies bytea option
+ * data from one MemoryConext to another without knowing about it's internal
+ * structure, so it would not be able to copy string values if they are outside
+ * of bytea memory chunk.
+ */
+static void *
+optionsAllocateBytea(options_catalog * catalog, List *options)
+{
+	Size		size;
+	int			i;
+	ListCell   *cell;
+	int			length;
+	void	   *res;
+
+	size = catalog->struct_size;
+
+	/* Calculate size needed to store all string values for this option */
+	for (i = 0; i < catalog->num; i++)
+	{
+		option_definition_basic *definition = catalog->definitions[i];
+		bool		found = false;
+		option_value *option;
+
+		/* Not interested in non-string options, skipping */
+		if (definition->type != OPTION_TYPE_STRING)
+			continue;
+
+		/*
+		 * Trying to find option_value that references definition catalog
+		 * entry
+		 */
+		foreach(cell, options)
+		{
+			option = (option_value *) lfirst(cell);
+			if (option->status == OPTION_VALUE_STATUS_PARSED &&
+				pg_strcasecmp(option->gen->name, definition->name) == 0)
+			{
+				found = true;
+				break;
+			}
+		}
+		if (found)
+			/* If found, it'value will be stored */
+			length = strlen(option->values.string_val) + 1;
+		else
+			/* If not found, then there would be default value there */
+		if (((option_definition_string *) definition)->default_val)
+			length = strlen(
+				 ((option_definition_string *) definition)->default_val) + 1;
+		else
+			length = 0;
+		/* Add total length of all string values to basic size */
+		size += length;
+	}
+
+	res = palloc0(size);
+	SET_VARSIZE(res, size);
+	return res;
+}
+
+/*
+ * optionsValuesToBytea
+ *		Converts options from List of option_values to binary bytea structure
+ *
+ * Convertation goes according to options_catalog: each catalog item
+ * has offset value, and option value in binary mode is written to the
+ * structure with that offset.
+ *
+ * More special case is string values. Memory for bytea structure is allocated
+ * by optionsAllocateBytea which adds some more space for string values to
+ * the size of original structure. All string values are copied there and
+ * inside the bytea structure an offset to that value is kept.
+ *
+ */
+static bytea *
+optionsValuesToBytea(List *options, options_catalog * catalog)
+{
+	char	   *data;
+	char	   *string_values_buffer;
+	int			i;
+
+	data = optionsAllocateBytea(catalog, options);
+
+	/* place for string data starts right after original structure */
+	string_values_buffer = data + catalog->struct_size;
+
+	for (i = 0; i < catalog->num; i++)
+	{
+		option_value *found = NULL;
+		ListCell   *cell;
+		char	   *item_pos;
+		option_definition_basic *definition = catalog->definitions[i];
+
+		if (definition->flags & OPTION_DEFINITION_FLAG_IGNORE)
+			continue;
+
+		/* Calculate the position of the item inside the structure */
+		item_pos = data + definition->struct_offset;
+
+		/* Looking for the corresponding option from options list */
+		foreach(cell, options)
+		{
+			option_value *option = (option_value *) lfirst(cell);
+
+			if (option->status == OPTION_VALUE_STATUS_RAW)
+				continue;		/* raw can come from db. Just ignore them then */
+			Assert(option->status != OPTION_VALUE_STATUS_EMPTY);
+
+			if (pg_strcasecmp(definition->name, option->gen->name) == 0)
+			{
+				found = option;
+				break;
+			}
+		}
+		/* writing to the proper position either option value or default val */
+		switch (definition->type)
+		{
+			case OPTION_TYPE_BOOL:
+				*(bool *) item_pos = found ?
+					found->values.bool_val :
+					((option_definition_bool *) definition)->default_val;
+				break;
+			case OPTION_TYPE_INT:
+				*(int *) item_pos = found ?
+					found->values.int_val :
+					((option_definition_int *) definition)->default_val;
+				break;
+			case OPTION_TYPE_REAL:
+				*(double *) item_pos = found ?
+					found->values.real_val :
+					((option_definition_real *) definition)->default_val;
+				break;
+			case OPTION_TYPE_ENUM:
+				*(int *) item_pos = found ?
+					found->values.enum_val :
+					((option_definition_enum *) definition)->default_val;
+				break;
+
+			case OPTION_TYPE_STRING:
+				{
+					/*
+					 * For string options: writing string value at the string
+					 * buffer after the structure, and storing and offset to
+					 * that value
+					 */
+					char	   *value = NULL;
+
+					if (found)
+						value = found->values.string_val;
+					else
+						value = ((option_definition_string *) definition)
+							->default_val;
+					*(int *) item_pos = value ?
+						string_values_buffer - data :
+						OPTION_STRING_VALUE_NOT_SET_OFFSET;
+					if (value)
+					{
+						strcpy(string_values_buffer, value);
+						string_values_buffer += strlen(value) + 1;
+					}
+				}
+				break;
+			default:
+				elog(ERROR, "unsupported reloption type %d",
+					 definition->type);
+				break;
+		}
+	}
+	return (void *) data;
+}
+
+
+/*
+ * transformOptions
+ *		This function is used by src/backend/commands/Xxxx in order to process
+ *		new option values, merge them with existing values (in the case of
+ *		ALTER command) and prepare to put them [back] into DB
+ */
+
+Datum
+transformOptions(options_catalog * catalog, Datum oldOptions,
+				 List *defList, options_parse_mode parse_mode)
+{
+	Datum		result;
+	List	   *new_values;
+	List	   *old_values;
+	List	   *merged_values;
+
+	/*
+	 * Parse and validate New values
+	 */
+	new_values = optionsDefListToRawValues(defList, parse_mode);
+	new_values = optionsParseRawValues(new_values, catalog,
+								   parse_mode | OPTIONS_PARSE_MODE_VALIDATE);
+
+	/*
+	 * Old values exists in case of ALTER commands. Transform them to raw
+	 * values and merge them with new_values, and parse it.
+	 */
+	if (PointerIsValid(DatumGetPointer(oldOptions)))
+	{
+		old_values = optionsTextArrayToRawValues(oldOptions);
+		merged_values = optionsMergeOptionValues(old_values, new_values);
+
+		/*
+		 * Parse options only after merging in order not to parse options that
+		 * would be removed by merging later
+		 */
+		merged_values = optionsParseRawValues(merged_values, catalog, 0);
+	}
+	else
+	{
+		merged_values = new_values;
+	}
+
+	/*
+	 * If we have postprocess_fun function defined in catalog, then there
+	 * might be some custom options checks there, with error throwing. So we
+	 * should do it here to throw these errors while CREATing or ALTERing
+	 * options
+	 */
+	if (catalog->postprocess_fun)
+	{
+		bytea	   *data = optionsValuesToBytea(merged_values, catalog);
+
+		catalog->postprocess_fun(data, true);
+		pfree(data);
+	}
+
+	/*
+	 * Convert options to TextArray format so caller can store them into
+	 * database
+	 */
+	result = optionsValuesToTextArray(merged_values);
+	return result;
+}
+
+
+/*
+ * optionsTextArrayToBytea
+ *		A meta-function that transforms options stored as TextArray into binary
+ *		(bytea) representation.
+ *
+ *	This function runs other transform functions that leads to the desired
+ *	result in no-validation mode. This function is used by cache mechanism,
+ *	in order to load and cache options when object itself is loaded and cached
+ */
+bytea *
+optionsTextArrayToBytea(options_catalog * catalog, Datum data)
+{
+	List	   *values;
+	bytea	   *options;
+
+	values = optionsTextArrayToRawValues(data);
+	values = optionsParseRawValues(values, catalog, 0);
+	options = optionsValuesToBytea(values, catalog);
+
+	if (catalog->postprocess_fun)
+	{
+		catalog->postprocess_fun(options, false);
+	}
+	return options;
+}
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index aa9c0f1..67a6257 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1,7 +1,10 @@
 /*-------------------------------------------------------------------------
  *
  * reloptions.c
- *	  Core support for relation options (pg_class.reloptions)
+ *	  Support for options relotions (pg_class.reloptions).
+ *	  Reloptions for non-Access Metod relations are defined here.
+ *	  There is also extractRelOptions function to extract reloptions
+ *	  from pg_class table for all kind of relations.
  *
  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
@@ -17,938 +20,17 @@
 
 #include <float.h>
 
-#include "access/gist_private.h"
-#include "access/hash.h"
 #include "access/htup_details.h"
-#include "access/nbtree.h"
+#include "access/options.h"
 #include "access/reloptions.h"
-#include "access/spgist.h"
 #include "access/tuptoaster.h"
-#include "catalog/pg_type.h"
-#include "commands/defrem.h"
 #include "commands/tablespace.h"
-#include "commands/view.h"
-#include "nodes/makefuncs.h"
-#include "postmaster/postmaster.h"
-#include "utils/array.h"
 #include "utils/attoptcache.h"
-#include "utils/builtins.h"
-#include "utils/guc.h"
-#include "utils/memutils.h"
 #include "utils/rel.h"
+#include "storage/bufmgr.h"
 
-/*
- * Contents of pg_class.reloptions
- *
- * To add an option:
- *
- * (i) decide on a type (integer, real, bool, string), name, default value,
- * upper and lower bounds (if applicable); for strings, consider a validation
- * routine.
- * (ii) add a record below (or use add_<type>_reloption).
- * (iii) add it to the appropriate options struct (perhaps StdRdOptions)
- * (iv) add it to the appropriate handling routine (perhaps
- * default_reloptions)
- * (v) make sure the lock level is set correctly for that operation
- * (vi) don't forget to document the option
- *
- * Note that we don't handle "oids" in relOpts because it is handled by
- * interpretOidsOption().
- *
- * The default choice for any new option should be AccessExclusiveLock.
- * In some cases the lock level can be reduced from there, but the lock
- * level chosen should always conflict with itself to ensure that multiple
- * changes aren't lost when we attempt concurrent changes.
- * The choice of lock level depends completely upon how that parameter
- * is used within the server, not upon how and when you'd like to change it.
- * Safety first. Existing choices are documented here, and elsewhere in
- * backend code where the parameters are used.
- *
- * In general, anything that affects the results obtained from a SELECT must be
- * protected by AccessExclusiveLock.
- *
- * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
- * since they are only used by the AV procs and don't change anything
- * currently executing.
- *
- * Fillfactor can be set because it applies only to subsequent changes made to
- * data blocks, as documented in heapio.c
- *
- * n_distinct options can be set at ShareUpdateExclusiveLock because they
- * are only used during ANALYZE, which uses a ShareUpdateExclusiveLock,
- * so the ANALYZE will not be affected by in-flight changes. Changing those
- * values has no affect until the next ANALYZE, so no need for stronger lock.
- *
- * Planner-related parameters can be set with ShareUpdateExclusiveLock because
- * they only affect planning and not the correctness of the execution. Plans
- * cannot be changed in mid-flight, so changes here could not easily result in
- * new improved plans in any case. So we allow existing queries to continue
- * and existing plans to survive, a small price to pay for allowing better
- * plans to be introduced concurrently without interfering with users.
- *
- * Setting parallel_workers is safe, since it acts the same as
- * max_parallel_workers_per_gather which is a USERSET parameter that doesn't
- * affect existing plans or queries.
- */
-
-static relopt_bool boolRelOpts[] =
-{
-	{
-		{
-			"autosummarize",
-			"Enables automatic summarization on this BRIN index",
-			RELOPT_KIND_BRIN,
-			AccessExclusiveLock
-		},
-		false
-	},
-	{
-		{
-			"autovacuum_enabled",
-			"Enables autovacuum in this relation",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		},
-		true
-	},
-	{
-		{
-			"user_catalog_table",
-			"Declare a table as an additional catalog table, e.g. for the purpose of logical replication",
-			RELOPT_KIND_HEAP,
-			AccessExclusiveLock
-		},
-		false
-	},
-	{
-		{
-			"fastupdate",
-			"Enables \"fast update\" feature for this GIN index",
-			RELOPT_KIND_GIN,
-			AccessExclusiveLock
-		},
-		true
-	},
-	{
-		{
-			"security_barrier",
-			"View acts as a row security barrier",
-			RELOPT_KIND_VIEW,
-			AccessExclusiveLock
-		},
-		false
-	},
-	/* list terminator */
-	{{NULL}}
-};
-
-static relopt_int intRelOpts[] =
-{
-	{
-		{
-			"fillfactor",
-			"Packs table pages only to this percentage",
-			RELOPT_KIND_HEAP,
-			ShareUpdateExclusiveLock	/* since it applies only to later
-										 * inserts */
-		},
-		HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100
-	},
-	{
-		{
-			"fillfactor",
-			"Packs btree index pages only to this percentage",
-			RELOPT_KIND_BTREE,
-			ShareUpdateExclusiveLock	/* since it applies only to later
-										 * inserts */
-		},
-		BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
-	},
-	{
-		{
-			"fillfactor",
-			"Packs hash index pages only to this percentage",
-			RELOPT_KIND_HASH,
-			ShareUpdateExclusiveLock	/* since it applies only to later
-										 * inserts */
-		},
-		HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100
-	},
-	{
-		{
-			"fillfactor",
-			"Packs gist index pages only to this percentage",
-			RELOPT_KIND_GIST,
-			ShareUpdateExclusiveLock	/* since it applies only to later
-										 * inserts */
-		},
-		GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
-	},
-	{
-		{
-			"fillfactor",
-			"Packs spgist index pages only to this percentage",
-			RELOPT_KIND_SPGIST,
-			ShareUpdateExclusiveLock	/* since it applies only to later
-										 * inserts */
-		},
-		SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100
-	},
-	{
-		{
-			"autovacuum_vacuum_threshold",
-			"Minimum number of tuple updates or deletes prior to vacuum",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		},
-		-1, 0, INT_MAX
-	},
-	{
-		{
-			"autovacuum_analyze_threshold",
-			"Minimum number of tuple inserts, updates or deletes prior to analyze",
-			RELOPT_KIND_HEAP,
-			ShareUpdateExclusiveLock
-		},
-		-1, 0, INT_MAX
-	},
-	{
-		{
-			"autovacuum_vacuum_cost_delay",
-			"Vacuum cost delay in milliseconds, for autovacuum",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		},
-		-1, 0, 100
-	},
-	{
-		{
-			"autovacuum_vacuum_cost_limit",
-			"Vacuum cost amount available before napping, for autovacuum",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		},
-		-1, 1, 10000
-	},
-	{
-		{
-			"autovacuum_freeze_min_age",
-			"Minimum age at which VACUUM should freeze a table row, for autovacuum",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		},
-		-1, 0, 1000000000
-	},
-	{
-		{
-			"autovacuum_multixact_freeze_min_age",
-			"Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		},
-		-1, 0, 1000000000
-	},
-	{
-		{
-			"autovacuum_freeze_max_age",
-			"Age at which to autovacuum a table to prevent transaction ID wraparound",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		},
-		-1, 100000, 2000000000
-	},
-	{
-		{
-			"autovacuum_multixact_freeze_max_age",
-			"Multixact age at which to autovacuum a table to prevent multixact wraparound",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		},
-		-1, 10000, 2000000000
-	},
-	{
-		{
-			"autovacuum_freeze_table_age",
-			"Age at which VACUUM should perform a full table sweep to freeze row versions",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		}, -1, 0, 2000000000
-	},
-	{
-		{
-			"autovacuum_multixact_freeze_table_age",
-			"Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		}, -1, 0, 2000000000
-	},
-	{
-		{
-			"log_autovacuum_min_duration",
-			"Sets the minimum execution time above which autovacuum actions will be logged",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		},
-		-1, -1, INT_MAX
-	},
-	{
-		{
-			"toast_tuple_target",
-			"Sets the target tuple length at which external columns will be toasted",
-			RELOPT_KIND_HEAP,
-			ShareUpdateExclusiveLock
-		},
-		TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
-	},
-	{
-		{
-			"pages_per_range",
-			"Number of pages that each page range covers in a BRIN index",
-			RELOPT_KIND_BRIN,
-			AccessExclusiveLock
-		}, 128, 1, 131072
-	},
-	{
-		{
-			"gin_pending_list_limit",
-			"Maximum size of the pending list for this GIN index, in kilobytes.",
-			RELOPT_KIND_GIN,
-			AccessExclusiveLock
-		},
-		-1, 64, MAX_KILOBYTES
-	},
-	{
-		{
-			"effective_io_concurrency",
-			"Number of simultaneous requests that can be handled efficiently by the disk subsystem.",
-			RELOPT_KIND_TABLESPACE,
-			ShareUpdateExclusiveLock
-		},
-#ifdef USE_PREFETCH
-		-1, 0, MAX_IO_CONCURRENCY
-#else
-		0, 0, 0
-#endif
-	},
-	{
-		{
-			"parallel_workers",
-			"Number of parallel processes that can be used per executor node for this relation.",
-			RELOPT_KIND_HEAP,
-			ShareUpdateExclusiveLock
-		},
-		-1, 0, 1024
-	},
-
-	/* list terminator */
-	{{NULL}}
-};
-
-static relopt_real realRelOpts[] =
-{
-	{
-		{
-			"autovacuum_vacuum_scale_factor",
-			"Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
-			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-			ShareUpdateExclusiveLock
-		},
-		-1, 0.0, 100.0
-	},
-	{
-		{
-			"autovacuum_analyze_scale_factor",
-			"Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
-			RELOPT_KIND_HEAP,
-			ShareUpdateExclusiveLock
-		},
-		-1, 0.0, 100.0
-	},
-	{
-		{
-			"seq_page_cost",
-			"Sets the planner's estimate of the cost of a sequentially fetched disk page.",
-			RELOPT_KIND_TABLESPACE,
-			ShareUpdateExclusiveLock
-		},
-		-1, 0.0, DBL_MAX
-	},
-	{
-		{
-			"random_page_cost",
-			"Sets the planner's estimate of the cost of a nonsequentially fetched disk page.",
-			RELOPT_KIND_TABLESPACE,
-			ShareUpdateExclusiveLock
-		},
-		-1, 0.0, DBL_MAX
-	},
-	{
-		{
-			"n_distinct",
-			"Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
-			RELOPT_KIND_ATTRIBUTE,
-			ShareUpdateExclusiveLock
-		},
-		0, -1.0, DBL_MAX
-	},
-	{
-		{
-			"n_distinct_inherited",
-			"Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
-			RELOPT_KIND_ATTRIBUTE,
-			ShareUpdateExclusiveLock
-		},
-		0, -1.0, DBL_MAX
-	},
-	/* list terminator */
-	{{NULL}}
-};
-
-static relopt_string stringRelOpts[] =
-{
-	{
-		{
-			"buffering",
-			"Enables buffering build for this GiST index",
-			RELOPT_KIND_GIST,
-			AccessExclusiveLock
-		},
-		4,
-		false,
-		gistValidateBufferingOption,
-		"auto"
-	},
-	{
-		{
-			"check_option",
-			"View has WITH CHECK OPTION defined (local or cascaded).",
-			RELOPT_KIND_VIEW,
-			AccessExclusiveLock
-		},
-		0,
-		true,
-		validateWithCheckOption,
-		NULL
-	},
-	/* list terminator */
-	{{NULL}}
-};
-
-static relopt_gen **relOpts = NULL;
-static bits32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;
-
-static int	num_custom_options = 0;
-static relopt_gen **custom_options = NULL;
-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);
-
-/*
- * initialize_reloptions
- *		initialization routine, must be called before parsing
- *
- * Initialize the relOpts array and fill each variable's type and name length.
- */
-static void
-initialize_reloptions(void)
-{
-	int			i;
-	int			j;
-
-	j = 0;
-	for (i = 0; boolRelOpts[i].gen.name; i++)
-	{
-		Assert(DoLockModesConflict(boolRelOpts[i].gen.lockmode,
-								   boolRelOpts[i].gen.lockmode));
-		j++;
-	}
-	for (i = 0; intRelOpts[i].gen.name; i++)
-	{
-		Assert(DoLockModesConflict(intRelOpts[i].gen.lockmode,
-								   intRelOpts[i].gen.lockmode));
-		j++;
-	}
-	for (i = 0; realRelOpts[i].gen.name; i++)
-	{
-		Assert(DoLockModesConflict(realRelOpts[i].gen.lockmode,
-								   realRelOpts[i].gen.lockmode));
-		j++;
-	}
-	for (i = 0; stringRelOpts[i].gen.name; i++)
-	{
-		Assert(DoLockModesConflict(stringRelOpts[i].gen.lockmode,
-								   stringRelOpts[i].gen.lockmode));
-		j++;
-	}
-	j += num_custom_options;
-
-	if (relOpts)
-		pfree(relOpts);
-	relOpts = MemoryContextAlloc(TopMemoryContext,
-								 (j + 1) * sizeof(relopt_gen *));
-
-	j = 0;
-	for (i = 0; boolRelOpts[i].gen.name; i++)
-	{
-		relOpts[j] = &boolRelOpts[i].gen;
-		relOpts[j]->type = RELOPT_TYPE_BOOL;
-		relOpts[j]->namelen = strlen(relOpts[j]->name);
-		j++;
-	}
-
-	for (i = 0; intRelOpts[i].gen.name; i++)
-	{
-		relOpts[j] = &intRelOpts[i].gen;
-		relOpts[j]->type = RELOPT_TYPE_INT;
-		relOpts[j]->namelen = strlen(relOpts[j]->name);
-		j++;
-	}
-
-	for (i = 0; realRelOpts[i].gen.name; i++)
-	{
-		relOpts[j] = &realRelOpts[i].gen;
-		relOpts[j]->type = RELOPT_TYPE_REAL;
-		relOpts[j]->namelen = strlen(relOpts[j]->name);
-		j++;
-	}
-
-	for (i = 0; stringRelOpts[i].gen.name; i++)
-	{
-		relOpts[j] = &stringRelOpts[i].gen;
-		relOpts[j]->type = RELOPT_TYPE_STRING;
-		relOpts[j]->namelen = strlen(relOpts[j]->name);
-		j++;
-	}
-
-	for (i = 0; i < num_custom_options; i++)
-	{
-		relOpts[j] = custom_options[i];
-		j++;
-	}
-
-	/* add a list terminator */
-	relOpts[j] = NULL;
-
-	/* flag the work is complete */
-	need_initialization = false;
-}
-
-/*
- * add_reloption_kind
- *		Create a new relopt_kind value, to be used in custom reloptions by
- *		user-defined AMs.
- */
-relopt_kind
-add_reloption_kind(void)
-{
-	/* don't hand out the last bit so that the enum's behavior is portable */
-	if (last_assigned_kind >= RELOPT_KIND_MAX)
-		ereport(ERROR,
-				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-				 errmsg("user-defined relation parameter types limit exceeded")));
-	last_assigned_kind <<= 1;
-	return (relopt_kind) last_assigned_kind;
-}
-
-/*
- * add_reloption
- *		Add an already-created custom reloption to the list, and recompute the
- *		main parser table.
- */
-static void
-add_reloption(relopt_gen *newoption)
-{
-	static int	max_custom_options = 0;
-
-	if (num_custom_options >= max_custom_options)
-	{
-		MemoryContext oldcxt;
-
-		oldcxt = MemoryContextSwitchTo(TopMemoryContext);
-
-		if (max_custom_options == 0)
-		{
-			max_custom_options = 8;
-			custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
-		}
-		else
-		{
-			max_custom_options *= 2;
-			custom_options = repalloc(custom_options,
-									  max_custom_options * sizeof(relopt_gen *));
-		}
-		MemoryContextSwitchTo(oldcxt);
-	}
-	custom_options[num_custom_options++] = newoption;
-
-	need_initialization = true;
-}
-
-/*
- * allocate_reloption
- *		Allocate a new reloption and initialize the type-agnostic fields
- *		(for types other than string)
- */
-static relopt_gen *
-allocate_reloption(bits32 kinds, int type, const char *name, const char *desc)
-{
-	MemoryContext oldcxt;
-	size_t		size;
-	relopt_gen *newoption;
-
-	oldcxt = MemoryContextSwitchTo(TopMemoryContext);
-
-	switch (type)
-	{
-		case RELOPT_TYPE_BOOL:
-			size = sizeof(relopt_bool);
-			break;
-		case RELOPT_TYPE_INT:
-			size = sizeof(relopt_int);
-			break;
-		case RELOPT_TYPE_REAL:
-			size = sizeof(relopt_real);
-			break;
-		case RELOPT_TYPE_STRING:
-			size = sizeof(relopt_string);
-			break;
-		default:
-			elog(ERROR, "unsupported reloption type %d", type);
-			return NULL;		/* keep compiler quiet */
-	}
-
-	newoption = palloc(size);
-
-	newoption->name = pstrdup(name);
-	if (desc)
-		newoption->desc = pstrdup(desc);
-	else
-		newoption->desc = NULL;
-	newoption->kinds = kinds;
-	newoption->namelen = strlen(name);
-	newoption->type = type;
-
-	MemoryContextSwitchTo(oldcxt);
-
-	return newoption;
-}
-
-/*
- * add_bool_reloption
- *		Add a new boolean reloption
- */
-void
-add_bool_reloption(bits32 kinds, const char *name, const char *desc, bool default_val)
-{
-	relopt_bool *newoption;
-
-	newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
-												   name, desc);
-	newoption->default_val = default_val;
-
-	add_reloption((relopt_gen *) newoption);
-}
-
-/*
- * add_int_reloption
- *		Add a new integer reloption
- */
-void
-add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
-				  int min_val, int max_val)
-{
-	relopt_int *newoption;
-
-	newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
-												  name, desc);
-	newoption->default_val = default_val;
-	newoption->min = min_val;
-	newoption->max = max_val;
-
-	add_reloption((relopt_gen *) newoption);
-}
-
-/*
- * add_real_reloption
- *		Add a new float reloption
- */
-void
-add_real_reloption(bits32 kinds, const char *name, const char *desc, double default_val,
-				   double min_val, double max_val)
-{
-	relopt_real *newoption;
-
-	newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
-												   name, desc);
-	newoption->default_val = default_val;
-	newoption->min = min_val;
-	newoption->max = max_val;
-
-	add_reloption((relopt_gen *) newoption);
-}
-
-/*
- * add_string_reloption
- *		Add a new string reloption
- *
- * "validator" is an optional function pointer that can be used to test the
- * validity of the values.  It must elog(ERROR) when the argument string is
- * not acceptable for the variable.  Note that the default value must pass
- * the validation.
- */
-void
-add_string_reloption(bits32 kinds, const char *name, const char *desc, const char *default_val,
-					 validate_string_relopt validator)
-{
-	relopt_string *newoption;
-
-	/* make sure the validator/default combination is sane */
-	if (validator)
-		(validator) (default_val);
-
-	newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
-													 name, desc);
-	newoption->validate_cb = validator;
-	if (default_val)
-	{
-		newoption->default_val = MemoryContextStrdup(TopMemoryContext,
-													 default_val);
-		newoption->default_len = strlen(default_val);
-		newoption->default_isnull = false;
-	}
-	else
-	{
-		newoption->default_val = "";
-		newoption->default_len = 0;
-		newoption->default_isnull = true;
-	}
-
-	add_reloption((relopt_gen *) newoption);
-}
-
-/*
- * Transform a relation options list (list of DefElem) into the text array
- * format that is kept in pg_class.reloptions, including only those options
- * that are in the passed namespace.  The output values do not include the
- * namespace.
- *
- * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
- * ALTER TABLE RESET.  In the ALTER cases, oldOptions is the existing
- * reloptions value (possibly NULL), and we replace or remove entries
- * as needed.
- *
- * If ignoreOids is true, then we should ignore any occurrence of "oids"
- * in the list (it will be or has been handled by interpretOidsOption()).
- *
- * Note that this is not responsible for determining whether the options
- * are valid, but it does check that namespaces for all the options given are
- * listed in validnsps.  The NULL namespace is always valid and need not be
- * explicitly listed.  Passing a NULL pointer means that only the NULL
- * namespace is valid.
- *
- * Both oldOptions and the result are text arrays (or NULL for "default"),
- * but we declare them as Datums to avoid including array.h in reloptions.h.
- */
-Datum
-transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
-					char *validnsps[], bool ignoreOids, bool isReset)
-{
-	Datum		result;
-	ArrayBuildState *astate;
-	ListCell   *cell;
-
-	/* no change if empty list */
-	if (defList == NIL)
-		return oldOptions;
-
-	/* We build new array using accumArrayResult */
-	astate = NULL;
-
-	/* Copy any oldOptions that aren't to be replaced */
-	if (PointerIsValid(DatumGetPointer(oldOptions)))
-	{
-		ArrayType  *array = DatumGetArrayTypeP(oldOptions);
-		Datum	   *oldoptions;
-		int			noldoptions;
-		int			i;
-
-		deconstruct_array(array, TEXTOID, -1, false, 'i',
-						  &oldoptions, NULL, &noldoptions);
-
-		for (i = 0; i < noldoptions; i++)
-		{
-			char	   *text_str = VARDATA(oldoptions[i]);
-			int			text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
-			/* Search for a match in defList */
-			foreach(cell, defList)
-			{
-				DefElem    *def = (DefElem *) lfirst(cell);
-				int			kw_len;
-
-				/* ignore if not in the same namespace */
-				if (namspace == NULL)
-				{
-					if (def->defnamespace != NULL)
-						continue;
-				}
-				else if (def->defnamespace == NULL)
-					continue;
-				else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
-					continue;
-
-				kw_len = strlen(def->defname);
-				if (text_len > kw_len && text_str[kw_len] == '=' &&
-					pg_strncasecmp(text_str, def->defname, kw_len) == 0)
-					break;
-			}
-			if (!cell)
-			{
-				/* No match, so keep old option */
-				astate = accumArrayResult(astate, oldoptions[i],
-										  false, TEXTOID,
-										  CurrentMemoryContext);
-			}
-		}
-	}
-
-	/*
-	 * If CREATE/SET, add new options to array; if RESET, just check that the
-	 * user didn't say RESET (option=val).  (Must do this because the grammar
-	 * doesn't enforce it.)
-	 */
-	foreach(cell, defList)
-	{
-		DefElem    *def = (DefElem *) lfirst(cell);
-
-		if (isReset)
-		{
-			if (def->arg != NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("RESET must not include values for parameters")));
-		}
-		else
-		{
-			text	   *t;
-			const char *value;
-			Size		len;
-
-			/*
-			 * Error out if the namespace is not valid.  A NULL namespace is
-			 * always valid.
-			 */
-			if (def->defnamespace != NULL)
-			{
-				bool		valid = false;
-				int			i;
-
-				if (validnsps)
-				{
-					for (i = 0; validnsps[i]; i++)
-					{
-						if (pg_strcasecmp(def->defnamespace,
-										  validnsps[i]) == 0)
-						{
-							valid = true;
-							break;
-						}
-					}
-				}
-
-				if (!valid)
-					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("unrecognized parameter namespace \"%s\"",
-									def->defnamespace)));
-			}
-
-			if (ignoreOids && pg_strcasecmp(def->defname, "oids") == 0)
-				continue;
-
-			/* ignore if not in the same namespace */
-			if (namspace == NULL)
-			{
-				if (def->defnamespace != NULL)
-					continue;
-			}
-			else if (def->defnamespace == NULL)
-				continue;
-			else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
-				continue;
-
-			/*
-			 * Flatten the DefElem into a text string like "name=arg". If we
-			 * have just "name", assume "name=true" is meant.  Note: the
-			 * namespace is not output.
-			 */
-			if (def->arg != NULL)
-				value = defGetString(def);
-			else
-				value = "true";
-			len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
-			/* +1 leaves room for sprintf's trailing null */
-			t = (text *) palloc(len + 1);
-			SET_VARSIZE(t, len);
-			sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
-			astate = accumArrayResult(astate, PointerGetDatum(t),
-									  false, TEXTOID,
-									  CurrentMemoryContext);
-		}
-	}
-
-	if (astate)
-		result = makeArrayResult(astate, CurrentMemoryContext);
-	else
-		result = (Datum) 0;
-
-	return result;
-}
-
-
-/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
- */
-List *
-untransformRelOptions(Datum options)
-{
-	List	   *result = NIL;
-	ArrayType  *array;
-	Datum	   *optiondatums;
-	int			noptions;
-	int			i;
-
-	/* Nothing to do if no options */
-	if (!PointerIsValid(DatumGetPointer(options)))
-		return result;
-
-	array = DatumGetArrayTypeP(options);
-
-	deconstruct_array(array, TEXTOID, -1, false, 'i',
-					  &optiondatums, NULL, &noptions);
-
-	for (i = 0; i < noptions; i++)
-	{
-		char	   *s;
-		char	   *p;
-		Node	   *val = NULL;
-
-		s = TextDatumGetCString(optiondatums[i]);
-		p = strchr(s, '=');
-		if (p)
-		{
-			*p++ = '\0';
-			val = (Node *) makeString(pstrdup(p));
-		}
-		result = lappend(result, makeDefElem(pstrdup(s), val, -1));
-	}
-
-	return result;
-}
+void add_autovacuum_options(options_catalog * catalog, int base_offset,
+					   bool for_toast);
 
 /*
  * Extract and parse reloptions from a pg_class tuple.
@@ -964,11 +46,12 @@ untransformRelOptions(Datum options)
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
-				  amoptions_function amoptions)
+				  amrelopt_catalog_function catalog_fun)
 {
-	bytea	   *options;
+	bytea	   *options = NULL;
 	bool		isnull;
 	Datum		datum;
+	options_catalog *catalog = NULL;
 	Form_pg_class classForm;
 
 	datum = fastgetattr(tuple,
@@ -984,550 +67,327 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 	switch (classForm->relkind)
 	{
 		case RELKIND_RELATION:
-		case RELKIND_TOASTVALUE:
 		case RELKIND_MATVIEW:
-		case RELKIND_PARTITIONED_TABLE:
-			options = heap_reloptions(classForm->relkind, datum, false);
+			catalog = get_heap_relopt_catalog();
+			break;
+		case RELKIND_TOASTVALUE:
+			catalog = get_toast_relopt_catalog();
 			break;
 		case RELKIND_VIEW:
-			options = view_reloptions(datum, false);
+			catalog = get_view_relopt_catalog();
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+			if (catalog_fun)
+				catalog = catalog_fun();
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
 			break;
+		case RELKIND_PARTITIONED_TABLE:
+			catalog = NULL; /* No options for parted table for now */
+			break;
 		default:
 			Assert(false);		/* can't get here */
 			options = NULL;		/* keep compiler quiet */
 			break;
 	}
 
+	if (catalog)
+		options = optionsTextArrayToBytea(catalog, datum);
 	return options;
 }
 
-/*
- * Interpret reloptions that are given in text-array format.
- *
- * options is a reloption text array as constructed by transformRelOptions.
- * kind specifies the family of options to be processed.
- *
- * The return value is a relopt_value * array on which the options actually
- * set in the options array are marked with isset=true.  The length of this
- * array is returned in *numrelopts.  Options not set are also present in the
- * array; this is so that the caller can easily locate the default values.
- *
- * If there are no options of the given kind, numrelopts is set to 0 and NULL
- * is returned (unless options are illegally supplied despite none being
- * defined, in which case an error occurs).
- *
- * Note: values of type int, bool and real are allocated as part of the
- * returned array.  Values of type string are allocated separately and must
- * be freed by the caller.
- */
-relopt_value *
-parseRelOptions(Datum options, bool validate, relopt_kind kind,
-				int *numrelopts)
-{
-	relopt_value *reloptions = NULL;
-	int			numoptions = 0;
-	int			i;
-	int			j;
-
-	if (need_initialization)
-		initialize_reloptions();
-
-	/* Build a list of expected options, based on kind */
-
-	for (i = 0; relOpts[i]; i++)
-		if (relOpts[i]->kinds & kind)
-			numoptions++;
-
-	if (numoptions > 0)
-	{
-		reloptions = palloc(numoptions * sizeof(relopt_value));
-
-		for (i = 0, j = 0; relOpts[i]; i++)
-		{
-			if (relOpts[i]->kinds & kind)
-			{
-				reloptions[j].gen = relOpts[i];
-				reloptions[j].isset = false;
-				j++;
-			}
-		}
-	}
-
-	/* Done if no options */
-	if (PointerIsValid(DatumGetPointer(options)))
-	{
-		ArrayType  *array = DatumGetArrayTypeP(options);
-		Datum	   *optiondatums;
-		int			noptions;
-
-		deconstruct_array(array, TEXTOID, -1, false, 'i',
-						  &optiondatums, NULL, &noptions);
-
-		for (i = 0; i < noptions; i++)
-		{
-			char	   *text_str = VARDATA(optiondatums[i]);
-			int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
-			int			j;
-
-			/* Search for a match in reloptions */
-			for (j = 0; j < numoptions; j++)
-			{
-				int			kw_len = reloptions[j].gen->namelen;
-
-				if (text_len > kw_len && text_str[kw_len] == '=' &&
-					pg_strncasecmp(text_str, reloptions[j].gen->name,
-								   kw_len) == 0)
-				{
-					parse_one_reloption(&reloptions[j], text_str, text_len,
-										validate);
-					break;
-				}
-			}
-
-			if (j >= numoptions && validate)
-			{
-				char	   *s;
-				char	   *p;
-
-				s = TextDatumGetCString(optiondatums[i]);
-				p = strchr(s, '=');
-				if (p)
-					*p = '\0';
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("unrecognized parameter \"%s\"", s)));
-			}
-		}
-
-		/* It's worth avoiding memory leaks in this function */
-		pfree(optiondatums);
-		if (((void *) array) != DatumGetPointer(options))
-			pfree(array);
-	}
-
-	*numrelopts = numoptions;
-	return reloptions;
-}
 
 /*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * Here goes functions that build catalogs for non-index relations. Catalogs
+ * for index relations are available from Access Methods, all other
+ * relation catalogs are here
  */
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
-					bool validate)
-{
-	char	   *value;
-	int			value_len;
-	bool		parsed;
-	bool		nofree = false;
-
-	if (option->isset && validate)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("parameter \"%s\" specified more than once",
-						option->gen->name)));
-
-	value_len = text_len - option->gen->namelen - 1;
-	value = (char *) palloc(value_len + 1);
-	memcpy(value, text_str + option->gen->namelen + 1, value_len);
-	value[value_len] = '\0';
-
-	switch (option->gen->type)
-	{
-		case RELOPT_TYPE_BOOL:
-			{
-				parsed = parse_bool(value, &option->values.bool_val);
-				if (validate && !parsed)
-					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("invalid value for boolean option \"%s\": %s",
-									option->gen->name, value)));
-			}
-			break;
-		case RELOPT_TYPE_INT:
-			{
-				relopt_int *optint = (relopt_int *) option->gen;
-
-				parsed = parse_int(value, &option->values.int_val, 0, NULL);
-				if (validate && !parsed)
-					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("invalid value for integer option \"%s\": %s",
-									option->gen->name, value)));
-				if (validate && (option->values.int_val < optint->min ||
-								 option->values.int_val > optint->max))
-					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("value %s out of bounds for option \"%s\"",
-									value, option->gen->name),
-							 errdetail("Valid values are between \"%d\" and \"%d\".",
-									   optint->min, optint->max)));
-			}
-			break;
-		case RELOPT_TYPE_REAL:
-			{
-				relopt_real *optreal = (relopt_real *) option->gen;
-
-				parsed = parse_real(value, &option->values.real_val);
-				if (validate && !parsed)
-					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("invalid value for floating point option \"%s\": %s",
-									option->gen->name, value)));
-				if (validate && (option->values.real_val < optreal->min ||
-								 option->values.real_val > optreal->max))
-					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("value %s out of bounds for option \"%s\"",
-									value, option->gen->name),
-							 errdetail("Valid values are between \"%f\" and \"%f\".",
-									   optreal->min, optreal->max)));
-			}
-			break;
-		case RELOPT_TYPE_STRING:
-			{
-				relopt_string *optstring = (relopt_string *) option->gen;
-
-				option->values.string_val = value;
-				nofree = true;
-				if (validate && optstring->validate_cb)
-					(optstring->validate_cb) (value);
-				parsed = true;
-			}
-			break;
-		default:
-			elog(ERROR, "unsupported reloption type %d", option->gen->type);
-			parsed = true;		/* quiet compiler */
-			break;
-	}
-
-	if (parsed)
-		option->isset = true;
-	if (!nofree)
-		pfree(value);
-}
 
 /*
- * Given the result from parseRelOptions, allocate a struct that's of the
- * specified base size plus any extra space that's needed for string variables.
+ * Both heap and toast relation have almost the same autovacuum set of options.
+ * These autovacuum options are stored in the same structure (AutoVacOpts)
+ * that is a part of both HeapOptions and ToastOptions.
  *
- * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
- * equivalent).
- */
-void *
-allocateReloptStruct(Size base, relopt_value *options, int numoptions)
-{
-	Size		size = base;
-	int			i;
-
-	for (i = 0; i < numoptions; i++)
-		if (options[i].gen->type == RELOPT_TYPE_STRING)
-			size += GET_STRING_RELOPTION_LEN(options[i]) + 1;
-
-	return palloc0(size);
-}
-
-/*
- * Given the result of parseRelOptions and a parsing table, fill in the
- * struct (previously allocated with allocateReloptStruct) with the parsed
- * values.
+ * Function add_autovacuum_options adds to the "catalog" catalog autovacuum
+ * options definition. In binary mode this options are saved into AutoVacOpts
+ * structure, which is located in result bytea chunk with base_offset offset.
  *
- * rdopts is the pointer to the allocated struct to be filled.
- * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
- * options, of length numoptions, is parseRelOptions' output.
- * elems, of length numelems, is the table describing the allowed options.
- * When validate is true, it is expected that all options appear in elems.
+ * Heap has two options (autovacuum_analyze_threshold and
+ * autovacuum_analyze_scale_factor) that is not used for toast. So for toast
+ * case this options are added to catalog with OPTION_DEFINITION_FLAG_REJECT
+ * flag set, so postgres will reject this toast options from the CREATE or
+ * ALTER command, but still will save default value in binary representation
  */
+
 void
-fillRelOptions(void *rdopts, Size basesize,
-			   relopt_value *options, int numoptions,
-			   bool validate,
-			   const relopt_parse_elt *elems, int numelems)
+add_autovacuum_options(options_catalog * catalog, int base_offset,
+					   bool for_toast)
 {
-	int			i;
-	int			offset = basesize;
-
-	for (i = 0; i < numoptions; i++)
-	{
-		int			j;
-		bool		found = false;
-
-		for (j = 0; j < numelems; j++)
-		{
-			if (pg_strcasecmp(options[i].gen->name, elems[j].optname) == 0)
-			{
-				relopt_string *optstring;
-				char	   *itempos = ((char *) rdopts) + elems[j].offset;
-				char	   *string_val;
-
-				switch (options[i].gen->type)
-				{
-					case RELOPT_TYPE_BOOL:
-						*(bool *) itempos = options[i].isset ?
-							options[i].values.bool_val :
-							((relopt_bool *) options[i].gen)->default_val;
-						break;
-					case RELOPT_TYPE_INT:
-						*(int *) itempos = options[i].isset ?
-							options[i].values.int_val :
-							((relopt_int *) options[i].gen)->default_val;
-						break;
-					case RELOPT_TYPE_REAL:
-						*(double *) itempos = options[i].isset ?
-							options[i].values.real_val :
-							((relopt_real *) options[i].gen)->default_val;
-						break;
-					case RELOPT_TYPE_STRING:
-						optstring = (relopt_string *) options[i].gen;
-						if (options[i].isset)
-							string_val = options[i].values.string_val;
-						else if (!optstring->default_isnull)
-							string_val = optstring->default_val;
-						else
-							string_val = NULL;
-
-						if (string_val == NULL)
-							*(int *) itempos = 0;
-						else
-						{
-							strcpy((char *) rdopts + offset, string_val);
-							*(int *) itempos = offset;
-							offset += strlen(string_val) + 1;
-						}
-						break;
-					default:
-						elog(ERROR, "unsupported reloption type %d",
-							 options[i].gen->type);
-						break;
-				}
-				found = true;
-				break;
-			}
-		}
-		if (validate && !found)
-			elog(ERROR, "reloption \"%s\" not found in parse table",
-				 options[i].gen->name);
-	}
-	SET_VARSIZE(rdopts, offset);
+	optionsCatalogAddItemBool(catalog, "autovacuum_enabled",
+							  "Enables autovacuum in this relation",
+							  ShareUpdateExclusiveLock,
+					  0, base_offset + offsetof(AutoVacOpts, enabled), true);
+
+	optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_threshold",
+				"Minimum number of tuple updates or deletes prior to vacuum",
+							 ShareUpdateExclusiveLock,
+					0, base_offset + offsetof(AutoVacOpts, vacuum_threshold),
+							 -1, 0, INT_MAX);
+
+	optionsCatalogAddItemInt(catalog, "autovacuum_analyze_threshold",
+				"Minimum number of tuple updates or deletes prior to vacuum",
+							 ShareUpdateExclusiveLock,
+							 for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0,
+					  base_offset + offsetof(AutoVacOpts, analyze_threshold),
+							 -1, 0, INT_MAX);
+
+	optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_cost_delay",
+						 "Vacuum cost delay in milliseconds, for autovacuum",
+							 ShareUpdateExclusiveLock,
+				   0, base_offset + offsetof(AutoVacOpts, vacuum_cost_delay),
+							 -1, 0, 100);
+
+	optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_cost_limit",
+			   "Vacuum cost amount available before napping, for autovacuum",
+							 ShareUpdateExclusiveLock,
+				   0, base_offset + offsetof(AutoVacOpts, vacuum_cost_limit),
+							 -1, 0, 10000);
+
+	optionsCatalogAddItemInt(catalog, "autovacuum_freeze_min_age",
+	 "Minimum age at which VACUUM should freeze a table row, for autovacuum",
+							 ShareUpdateExclusiveLock,
+					  0, base_offset + offsetof(AutoVacOpts, freeze_min_age),
+							 -1, 0, 1000000000);
+
+	optionsCatalogAddItemInt(catalog, "autovacuum_freeze_max_age",
+	"Age at which to autovacuum a table to prevent transaction ID wraparound",
+							 ShareUpdateExclusiveLock,
+					  0, base_offset + offsetof(AutoVacOpts, freeze_max_age),
+							 -1, 100000, 2000000000);
+
+	optionsCatalogAddItemInt(catalog, "autovacuum_freeze_table_age",
+							 "Age at which VACUUM should perform a full table sweep to freeze row versions",
+							 ShareUpdateExclusiveLock,
+					0, base_offset + offsetof(AutoVacOpts, freeze_table_age),
+							 -1, 0, 2000000000);
+
+	optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_min_age",
+							 "Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
+							 ShareUpdateExclusiveLock,
+			0, base_offset + offsetof(AutoVacOpts, multixact_freeze_min_age),
+							 -1, 0, 1000000000);
+
+	optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_max_age",
+							 "Multixact age at which to autovacuum a table to prevent multixact wraparound",
+							 ShareUpdateExclusiveLock,
+			0, base_offset + offsetof(AutoVacOpts, multixact_freeze_max_age),
+							 -1, 10000, 2000000000);
+
+	optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_table_age",
+							 "Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
+							 ShareUpdateExclusiveLock,
+		  0, base_offset + offsetof(AutoVacOpts, multixact_freeze_table_age),
+							 -1, 0, 2000000000);
+
+	optionsCatalogAddItemInt(catalog, "log_autovacuum_min_duration",
+							 "Sets the minimum execution time above which autovacuum actions will be logged",
+							 ShareUpdateExclusiveLock,
+					0, base_offset + offsetof(AutoVacOpts, log_min_duration),
+							 -1, -1, INT_MAX);
+
+	optionsCatalogAddItemReal(catalog, "autovacuum_vacuum_scale_factor",
+							  "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
+							  ShareUpdateExclusiveLock,
+				 0, base_offset + offsetof(AutoVacOpts, vacuum_scale_factor),
+							  -1, 0.0, 100.0);
+
+	optionsCatalogAddItemReal(catalog, "autovacuum_analyze_scale_factor",
+							  "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
+							  ShareUpdateExclusiveLock,
+							  for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0,
+				   base_offset + offsetof(AutoVacOpts, analyze_scale_factor),
+							  -1, 0.0, 100.0);
 }
 
-
 /*
- * Option parser for anything that uses StdRdOptions.
+ * get_heap_relopt_catalog
+ *		Returns an options catalog for heap relation.
  */
-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,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
-		{"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)},
-		{"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
-		{"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)},
-		{"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
-		{"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
-		{"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
-		{"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
-		{"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age)},
-		{"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)},
-		{"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
-		{"log_autovacuum_min_duration", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)},
-		{"toast_tuple_target", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, toast_tuple_target)},
-		{"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
-		{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)},
-		{"user_catalog_table", RELOPT_TYPE_BOOL,
-		offsetof(StdRdOptions, user_catalog_table)},
-		{"parallel_workers", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, parallel_workers)}
-	};
+static options_catalog *heap_relopt_catalog = NULL;
 
-	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;
+options_catalog *
+get_heap_relopt_catalog(void)
+{
+	if (!heap_relopt_catalog)
+	{
+		heap_relopt_catalog = allocateOptionsCatalog(NULL, sizeof(HeapOptions),
+													 14 + 5);	/* 14 - for autovacuum
+																 * options, 4 - core
+																 * heap options */
+
+		optionsCatalogAddItemInt(heap_relopt_catalog, "fillfactor",
+								 "Packs table pages only to this percentag",
+								 ShareUpdateExclusiveLock,		/* since it applies only
+																 * to later inserts */
+								 0, offsetof(HeapOptions, fillfactor),
+						  HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100);
+
+		optionsCatalogAddItemInt(heap_relopt_catalog, "toast_tuple_target",
+								 "Sets the target tuple length at which external columns will be toasted",
+								 ShareUpdateExclusiveLock,
+								 0, offsetof(HeapOptions, toast_tuple_target),
+						  TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN);
+
+		add_autovacuum_options(heap_relopt_catalog,
+							   offsetof(HeapOptions, autovacuum), false);
+
+		optionsCatalogAddItemBool(heap_relopt_catalog, "user_catalog_table",
+								  "Declare a table as an additional catalog table, e.g. for the purpose of logical replication",
+								  AccessExclusiveLock,
+								0, offsetof(HeapOptions, user_catalog_table),
+								  false);
+
+		optionsCatalogAddItemInt(heap_relopt_catalog, "parallel_workers",
+								 "Number of parallel processes that can be used per executor node for this relation",
+								 ShareUpdateExclusiveLock,
+								 0, offsetof(HeapOptions, parallel_workers),
+								 -1, 0, 1024);
+
+		/*
+		 * A WITH OID / WITHOUT OIDS expressions are converted by syntax
+		 * parser into "oids" relation option, but this option is processed by
+		 * interpretOidsOption(), that does not use core options code. That is
+		 * why here we should just ignore this option as if it does not exist.
+		 * Do it with OPTION_DEFINITION_FLAG_IGNORE flag
+		 */
+		optionsCatalogAddItemBool(heap_relopt_catalog, "oids",
+				 "Option used for WITH OIDS expression. Processed elsewhere",
+								  NoLock,
+								  OPTION_DEFINITION_FLAG_IGNORE, -1,
+								  false);
+	}
+	return heap_relopt_catalog;
 }
 
 /*
- * Option parser for views
+ * get_toast_relopt_catalog
+ *		Returns an options catalog for toast relation.
  */
-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)},
-		{"check_option", RELOPT_TYPE_STRING,
-		offsetof(ViewOptions, check_option_offset)}
-	};
-
-	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);
+static options_catalog *toast_relopt_catalog = NULL;
 
-	fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numoptions,
-				   validate, tab, lengthof(tab));
-
-	pfree(options);
-
-	return (bytea *) vopts;
-}
-
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_catalog *
+get_toast_relopt_catalog(void)
 {
-	StdRdOptions *rdopts;
-
-	switch (relkind)
+	if (!toast_relopt_catalog)
 	{
-		case RELKIND_TOASTVALUE:
-			rdopts = (StdRdOptions *)
-				default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
-			if (rdopts != NULL)
-			{
-				/* adjust default-only parameters for TOAST relations */
-				rdopts->fillfactor = 100;
-				rdopts->autovacuum.analyze_threshold = -1;
-				rdopts->autovacuum.analyze_scale_factor = -1;
-			}
-			return (bytea *) rdopts;
-		case RELKIND_RELATION:
-		case RELKIND_MATVIEW:
-			return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
-		case RELKIND_PARTITIONED_TABLE:
-			return default_reloptions(reloptions, validate,
-									  RELOPT_KIND_PARTITIONED);
-		default:
-			/* other relkinds are not supported */
-			return NULL;
+		toast_relopt_catalog = allocateOptionsCatalog("toast", sizeof(ToastOptions),
+													  14);		/* 14 - for autovacuum
+																 * options */
+		add_autovacuum_options(toast_relopt_catalog,
+							   offsetof(ToastOptions, autovacuum), true);
 	}
+	return toast_relopt_catalog;
 }
 
-
 /*
- * Parse options for indexes.
- *
- *	amoptions	index AM's option parser function
- *	reloptions	options as text[] datum
- *	validate	error flag
+ * get_view_relopt_catalog
+ *		Returns an options catalog for view relation.
  */
-bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
+static options_catalog *view_relopt_catalog = NULL;
+
+options_catalog *
+get_view_relopt_catalog(void)
 {
-	Assert(amoptions != NULL);
+	static const char *enum_names[] = VIEW_OPTION_CHECK_OPTION_VALUE_NAMES;
 
-	/* Assume function is strict */
-	if (!PointerIsValid(DatumGetPointer(reloptions)))
-		return NULL;
+	if (!view_relopt_catalog)
+	{
+		view_relopt_catalog = allocateOptionsCatalog(NULL,
+													 sizeof(ViewOptions), 2);
 
-	return amoptions(reloptions, validate);
+		optionsCatalogAddItemBool(view_relopt_catalog, "security_barrier",
+								  "View acts as a row security barrier",
+								  AccessExclusiveLock,
+						  0, offsetof(ViewOptions, security_barrier), false);
+
+		optionsCatalogAddItemEnum(view_relopt_catalog, "check_option",
+					"View has WITH CHECK OPTION defined (local or cascaded)",
+								  AccessExclusiveLock, 0,
+								  offsetof(ViewOptions, check_option),
+								  enum_names,
+								  VIEW_OPTION_CHECK_OPTION_NOT_SET);
+	}
+	return view_relopt_catalog;
 }
 
 /*
- * Option parser for attribute reloptions
+ * get_attribute_options_catalog
+ *		Returns an options catalog for heap attributes
  */
-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);
+static options_catalog *attribute_options_catalog = NULL;
 
-	fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
-				   validate, tab, lengthof(tab));
+options_catalog *
+get_attribute_options_catalog(void)
+{
+	if (!attribute_options_catalog)
+	{
+		attribute_options_catalog = allocateOptionsCatalog(NULL,
+												   sizeof(AttributeOpts), 2);
 
-	pfree(options);
+		optionsCatalogAddItemReal(attribute_options_catalog, "n_distinct",
+								  "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations)",
+								  ShareUpdateExclusiveLock,
+				   0, offsetof(AttributeOpts, n_distinct), 0, -1.0, DBL_MAX);
 
-	return (bytea *) aopts;
+		optionsCatalogAddItemReal(attribute_options_catalog,
+								  "n_distinct_inherited",
+								  "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations",
+								  ShareUpdateExclusiveLock,
+		 0, offsetof(AttributeOpts, n_distinct_inherited), 0, -1.0, DBL_MAX);
+	}
+	return attribute_options_catalog;
 }
 
 /*
- * Option parser for tablespace reloptions
+ * get_tablespace_options_catalog
+ *		Returns an options catalog for tablespaces
  */
-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);
+static options_catalog *tablespace_options_catalog = NULL;
 
-	return (bytea *) tsopts;
+options_catalog *
+get_tablespace_options_catalog(void)
+{
+	if (!tablespace_options_catalog)
+	{
+		tablespace_options_catalog = allocateOptionsCatalog(NULL,
+												  sizeof(TableSpaceOpts), 3);
+
+		optionsCatalogAddItemReal(tablespace_options_catalog,
+								  "random_page_cost",
+								  "Sets the planner's estimate of the cost of a nonsequentially fetched disk page",
+								  ShareUpdateExclusiveLock,
+			0, offsetof(TableSpaceOpts, random_page_cost), -1, 0.0, DBL_MAX);
+
+		optionsCatalogAddItemReal(tablespace_options_catalog, "seq_page_cost",
+								  "Sets the planner's estimate of the cost of a sequentially fetched disk page",
+								  ShareUpdateExclusiveLock,
+			   0, offsetof(TableSpaceOpts, seq_page_cost), -1, 0.0, DBL_MAX);
+
+		optionsCatalogAddItemInt(tablespace_options_catalog,
+								 "effective_io_concurrency",
+								 "Number of simultaneous requests that can be handled efficiently by the disk subsystem",
+								 ShareUpdateExclusiveLock,
+					   0, offsetof(TableSpaceOpts, effective_io_concurrency),
+#ifdef USE_PREFETCH
+								 -1, 0, MAX_IO_CONCURRENCY
+#else
+								 0, 0, 0
+#endif
+			);
+	}
+	return tablespace_options_catalog;
 }
 
 /*
@@ -1537,33 +397,56 @@ tablespace_reloptions(Datum reloptions, bool validate)
  * for a longer explanation of how this works.
  */
 LOCKMODE
-AlterTableGetRelOptionsLockLevel(List *defList)
+AlterTableGetRelOptionsLockLevel(Relation rel, List *defList)
 {
 	LOCKMODE	lockmode = NoLock;
 	ListCell   *cell;
+	options_catalog *catalog = NULL;
 
 	if (defList == NIL)
 		return AccessExclusiveLock;
 
-	if (need_initialization)
-		initialize_reloptions();
+	switch (rel->rd_rel->relkind)
+	{
+		case RELKIND_TOASTVALUE:
+			catalog = get_toast_relopt_catalog();
+			break;
+		case RELKIND_RELATION:
+		case RELKIND_MATVIEW:
+			catalog = get_heap_relopt_catalog();
+			break;
+		case RELKIND_INDEX:
+			catalog = rel->rd_amroutine->amrelopt_catalog();
+			break;
+		case RELKIND_VIEW:
+			catalog = get_view_relopt_catalog();
+			break;
+		case RELKIND_PARTITIONED_TABLE:
+			catalog = NULL; /* No options for parted table for now */
+			break;
+		default:
+			Assert(false);		/* can't get here */
+			break;
+	}
+
+	Assert(catalog);			/* No catalog - no reloption change. Should
+								 * not get here */
 
 	foreach(cell, defList)
 	{
 		DefElem    *def = (DefElem *) lfirst(cell);
+
 		int			i;
 
-		for (i = 0; relOpts[i]; i++)
+		for (i = 0; i < catalog->num; i++)
 		{
-			if (pg_strncasecmp(relOpts[i]->name,
-							   def->defname,
-							   relOpts[i]->namelen + 1) == 0)
-			{
-				if (lockmode < relOpts[i]->lockmode)
-					lockmode = relOpts[i]->lockmode;
-			}
+			option_definition_basic *gen = catalog->definitions[i];
+
+			if (pg_strcasecmp(gen->name,
+							  def->defname) == 0)
+				if (lockmode < gen->lockmode)
+					lockmode = gen->lockmode;
 		}
 	}
-
 	return lockmode;
 }
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index d9c6483..8de47ed 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -16,7 +16,7 @@
 
 #include "access/gin_private.h"
 #include "access/ginxlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
 #include "access/xloginsert.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
@@ -26,7 +26,8 @@
 #include "utils/builtins.h"
 #include "utils/index_selfuncs.h"
 #include "utils/typcache.h"
-
+#include "utils/memutils.h"
+#include "utils/guc.h"
 
 /*
  * GIN handler function: return IndexAmRoutine with access method parameters
@@ -60,7 +61,6 @@ ginhandler(PG_FUNCTION_ARGS)
 	amroutine->amvacuumcleanup = ginvacuumcleanup;
 	amroutine->amcanreturn = NULL;
 	amroutine->amcostestimate = gincostestimate;
-	amroutine->amoptions = ginoptions;
 	amroutine->amproperty = NULL;
 	amroutine->amvalidate = ginvalidate;
 	amroutine->ambeginscan = ginbeginscan;
@@ -70,6 +70,7 @@ ginhandler(PG_FUNCTION_ARGS)
 	amroutine->amendscan = ginendscan;
 	amroutine->ammarkpos = NULL;
 	amroutine->amrestrpos = NULL;
+	amroutine->amrelopt_catalog = gingetreloptcatalog;
 	amroutine->amestimateparallelscan = NULL;
 	amroutine->aminitparallelscan = NULL;
 	amroutine->amparallelrescan = NULL;
@@ -603,35 +604,6 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
 	return entries;
 }
 
-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;
-}
-
 /*
  * Fetch index's statistical data into *stats
  *
@@ -718,3 +690,32 @@ ginUpdateStats(Relation index, const GinStatsData *stats)
 
 	END_CRIT_SECTION();
 }
+
+
+static options_catalog *gin_relopt_catalog = NULL;
+
+void *
+gingetreloptcatalog(void)
+{
+	if (!gin_relopt_catalog)
+	{
+		gin_relopt_catalog = allocateOptionsCatalog(NULL,
+													sizeof(GinOptions), 2);
+
+		optionsCatalogAddItemBool(gin_relopt_catalog, "fastupdate",
+						"Enables \"fast update\" feature for this GIN index",
+								  AccessExclusiveLock,
+								  0,
+								  offsetof(GinOptions, useFastUpdate),
+								  GIN_DEFAULT_USE_FASTUPDATE);
+
+		optionsCatalogAddItemInt(gin_relopt_catalog, "gin_pending_list_limit",
+		 "Maximum size of the pending list for this GIN index, in kilobytes",
+								 AccessExclusiveLock,
+								 0,
+								 offsetof(GinOptions, pendingListCleanupSize),
+								 -1, 64, MAX_KILOBYTES);
+
+	}
+	return gin_relopt_catalog;
+}
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index cf4b319..262f4f2 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -81,7 +81,6 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amvacuumcleanup = gistvacuumcleanup;
 	amroutine->amcanreturn = gistcanreturn;
 	amroutine->amcostestimate = gistcostestimate;
-	amroutine->amoptions = gistoptions;
 	amroutine->amproperty = gistproperty;
 	amroutine->amvalidate = gistvalidate;
 	amroutine->ambeginscan = gistbeginscan;
@@ -91,6 +90,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amendscan = gistendscan;
 	amroutine->ammarkpos = NULL;
 	amroutine->amrestrpos = NULL;
+	amroutine->amrelopt_catalog = gistgetreloptcatalog;
 	amroutine->amestimateparallelscan = NULL;
 	amroutine->aminitparallelscan = NULL;
 	amroutine->amparallelrescan = NULL;
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index 2415f00..d80d604 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -126,11 +126,10 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 	{
 		/* Get buffering mode from the options string */
 		GiSTOptions *options = (GiSTOptions *) index->rd_options;
-		char	   *bufferingMode = (char *) options + options->bufferingModeOffset;
 
-		if (strcmp(bufferingMode, "on") == 0)
+		if (options->buffering_mode == GIST_OPTION_BUFFERING_ON)
 			buildstate.bufferingMode = GIST_BUFFERING_STATS;
-		else if (strcmp(bufferingMode, "off") == 0)
+		else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF)
 			buildstate.bufferingMode = GIST_BUFFERING_DISABLED;
 		else
 			buildstate.bufferingMode = GIST_BUFFERING_AUTO;
@@ -234,25 +233,6 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 }
 
 /*
- * Validator for "buffering" reloption on GiST indexes. Allows "on", "off"
- * and "auto" values.
- */
-void
-gistValidateBufferingOption(const char *value)
-{
-	if (value == NULL ||
-		(strcmp(value, "on") != 0 &&
-		 strcmp(value, "off") != 0 &&
-		 strcmp(value, "auto") != 0))
-	{
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("invalid value for \"buffering\" option"),
-				 errdetail("Valid values are \"on\", \"off\", and \"auto\".")));
-	}
-}
-
-/*
  * Attempt to switch to buffering mode.
  *
  * If there is not enough memory for buffering build, sets bufferingMode
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index d8d1c0a..154f36f 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -18,14 +18,13 @@
 
 #include "access/gist_private.h"
 #include "access/htup_details.h"
-#include "access/reloptions.h"
+#include "access/options.h"
 #include "catalog/pg_opclass.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "utils/builtins.h"
 #include "utils/syscache.h"
 
-
 /*
  * Write itup vector to page, has no control of free space.
  */
@@ -832,34 +831,6 @@ gistNewBuffer(Relation r)
 	return buffer;
 }
 
-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_STRING, offsetof(GiSTOptions, bufferingModeOffset)}
-	};
-
-	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;
-}
-
 /*
  *	gistproperty() -- Check boolean properties of indexes.
  *
@@ -999,3 +970,33 @@ gistGetFakeLSN(Relation rel)
 		return GetFakeLSNForUnloggedRel();
 	}
 }
+
+static options_catalog *gist_relopt_catalog = NULL;
+
+void *
+gistgetreloptcatalog(void)
+{
+	static const char *enum_names[] = GIST_OPTION_BUFFERING_VALUE_NAMES;
+
+	if (!gist_relopt_catalog)
+	{
+		gist_relopt_catalog = allocateOptionsCatalog(NULL,
+													 sizeof(GiSTOptions), 2);
+
+		optionsCatalogAddItemInt(gist_relopt_catalog, "fillfactor",
+							"Packs gist index pages only to this percentage",
+								 NoLock,		/* No ALTER, no lock */
+								 OPTION_DEFINITION_FLAG_FORBID_ALTER,
+								 offsetof(GiSTOptions, fillfactor),
+								 GIST_DEFAULT_FILLFACTOR,
+								 GIST_MIN_FILLFACTOR, 100);
+		optionsCatalogAddItemEnum(gist_relopt_catalog, "buffering",
+							   "Enables buffering build for this GiST index",
+								  NoLock,		/* No ALTER, no lock */
+								  OPTION_DEFINITION_FLAG_FORBID_ALTER,
+								  offsetof(GiSTOptions, buffering_mode),
+								  enum_names,
+								  GIST_OPTION_BUFFERING_AUTO);
+	}
+	return gist_relopt_catalog;
+}
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 0fef60a..75c0ba9 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -79,7 +79,6 @@ hashhandler(PG_FUNCTION_ARGS)
 	amroutine->amvacuumcleanup = hashvacuumcleanup;
 	amroutine->amcanreturn = NULL;
 	amroutine->amcostestimate = hashcostestimate;
-	amroutine->amoptions = hashoptions;
 	amroutine->amproperty = NULL;
 	amroutine->amvalidate = hashvalidate;
 	amroutine->ambeginscan = hashbeginscan;
@@ -89,6 +88,7 @@ hashhandler(PG_FUNCTION_ARGS)
 	amroutine->amendscan = hashendscan;
 	amroutine->ammarkpos = NULL;
 	amroutine->amrestrpos = NULL;
+	amroutine->amrelopt_catalog = hashgetreloptcatalog;
 	amroutine->amestimateparallelscan = NULL;
 	amroutine->aminitparallelscan = NULL;
 	amroutine->amparallelrescan = NULL;
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index a50e35d..dc001b2 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -368,7 +368,7 @@ _hash_init(Relation rel, double num_tuples, ForkNumber forkNum)
 	data_width = sizeof(uint32);
 	item_width = MAXALIGN(sizeof(IndexTupleData)) + MAXALIGN(data_width) +
 		sizeof(ItemIdData);		/* include the line pointer */
-	ffactor = RelationGetTargetPageUsage(rel, HASH_DEFAULT_FILLFACTOR) / item_width;
+	ffactor = (BLCKSZ * HashGetFillFactor(rel) / 100) / item_width;
 	/* keep to a sane range */
 	if (ffactor < 10)
 		ffactor = 10;
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
index f2a1c5b..eb45362 100644
--- a/src/backend/access/hash/hashutil.c
+++ b/src/backend/access/hash/hashutil.c
@@ -15,7 +15,7 @@
 #include "postgres.h"
 
 #include "access/hash.h"
-#include "access/reloptions.h"
+#include "access/options.h"
 #include "access/relscan.h"
 #include "utils/lsyscache.h"
 #include "utils/rel.h"
@@ -286,12 +286,6 @@ _hash_checkpage(Relation rel, Buffer buf, int flags)
 	}
 }
 
-bytea *
-hashoptions(Datum reloptions, bool validate)
-{
-	return default_reloptions(reloptions, validate, RELOPT_KIND_HASH);
-}
-
 /*
  * _hash_get_indextuple_hashkey - get the hash index tuple's hash key value
  */
@@ -627,3 +621,28 @@ _hash_kill_items(IndexScanDesc scan)
 	else
 		_hash_relbuf(rel, buf);
 }
+
+static options_catalog *hash_relopt_catalog = NULL;
+
+void *
+hashgetreloptcatalog(void)
+{
+	if (!hash_relopt_catalog)
+	{
+		hash_relopt_catalog = allocateOptionsCatalog(NULL,
+												  sizeof(HashRelOptions), 1);
+
+		optionsCatalogAddItemInt(hash_relopt_catalog, "fillfactor",
+							"Packs hash index pages only to this percentage",
+								 NoLock,		/* No ALTER -- no lock */
+								 OPTION_DEFINITION_FLAG_FORBID_ALTER,	/* fillfactor is actualy
+																		 * stored in metapage
+																		 * and should not be
+																		 * changed once index is
+																		 * created */
+								 offsetof(HashRelOptions, fillfactor),
+								 HASH_DEFAULT_FILLFACTOR,
+								 HASH_MIN_FILLFACTOR, 100);
+	}
+	return hash_relopt_catalog;
+}
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 3acef27..5e31684 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2689,7 +2689,10 @@ heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
 	bool		need_cids = RelationIsAccessibleInLogicalDecoding(relation);
 
 	needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
-	saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
+	if (IsToastRelation(relation))
+		saveFreeSpace = ToastGetTargetPageFreeSpace();
+	else
+		saveFreeSpace = HeapGetTargetPageFreeSpace(relation,
 												   HEAP_DEFAULT_FILLFACTOR);
 
 	/* Toast and set header data in all the tuples */
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c
index 13e3bdc..8c8f023 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -323,7 +323,10 @@ RelationGetBufferForTuple(Relation relation, Size len,
 						len, MaxHeapTupleSize)));
 
 	/* Compute desired extra freespace due to fillfactor option */
-	saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
+	if (IsToastRelation(relation))
+		saveFreeSpace = ToastGetTargetPageFreeSpace();
+	else
+		saveFreeSpace = HeapGetTargetPageFreeSpace(relation,
 												   HEAP_DEFAULT_FILLFACTOR);
 
 	if (otherBuffer != InvalidBuffer)
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 9f33e0c..e9fa5a5 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -130,7 +130,11 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
 	 * important than sometimes getting a wrong answer in what is after all
 	 * just a heuristic estimate.
 	 */
-	minfree = RelationGetTargetPageFreeSpace(relation,
+
+	if (IsToastRelation(relation))
+		minfree = ToastGetTargetPageFreeSpace();
+	else
+		minfree = HeapGetTargetPageFreeSpace(relation,
 											 HEAP_DEFAULT_FILLFACTOR);
 	minfree = Max(minfree, BLCKSZ / 10);
 
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index f93c194..71bf1a1 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -667,7 +667,10 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
 						len, MaxHeapTupleSize)));
 
 	/* Compute desired extra freespace due to fillfactor option */
-	saveFreeSpace = RelationGetTargetPageFreeSpace(state->rs_new_rel,
+	if (IsToastRelation(state->rs_new_rel))
+		saveFreeSpace = ToastGetTargetPageFreeSpace();
+	else
+		saveFreeSpace = HeapGetTargetPageFreeSpace(state->rs_new_rel,
 												   HEAP_DEFAULT_FILLFACTOR);
 
 	/* Now we can check to see if there's enough free space already. */
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index c74945a..18ee03e 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -727,7 +727,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 		hoff += sizeof(Oid);
 	hoff = MAXALIGN(hoff);
 	/* now convert to a limit on the tuple data size */
-	maxDataLen = RelationGetToastTupleTarget(rel, TOAST_TUPLE_TARGET) - hoff;
+	maxDataLen = HeapGetToastTupleTarget(rel, TOAST_TUPLE_TARGET) - hoff;
 
 	/*
 	 * Look for attributes with attstorage 'x' to compress.  Also find large
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 310589d..164fe0a 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -1427,8 +1427,7 @@ _bt_findsplitloc(Relation rel,
 	state.is_rightmost = P_RIGHTMOST(opaque);
 	state.have_split = false;
 	if (state.is_leaf)
-		state.fillfactor = RelationGetFillFactor(rel,
-												 BTREE_DEFAULT_FILLFACTOR);
+		state.fillfactor = BTGetFillFactor(rel);
 	else
 		state.fillfactor = BTREE_NONLEAF_FILLFACTOR;
 	state.newitemonleft = false;	/* these just to keep compiler quiet */
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 399e6a1..45e6327 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -21,6 +21,7 @@
 #include "access/nbtree.h"
 #include "access/relscan.h"
 #include "access/xlog.h"
+#include "access/reloptions.h"
 #include "catalog/index.h"
 #include "commands/vacuum.h"
 #include "pgstat.h"
@@ -34,7 +35,6 @@
 #include "utils/index_selfuncs.h"
 #include "utils/memutils.h"
 
-
 /* Working state for btbuild and its callback */
 typedef struct
 {
@@ -115,8 +115,7 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 			 BTCycleId cycleid);
 static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
 			 BlockNumber orig_blkno);
-
-
+static void * btgetreloptcatalog (void);
 /*
  * Btree handler function: return IndexAmRoutine with access method parameters
  * and callbacks.
@@ -149,7 +148,6 @@ bthandler(PG_FUNCTION_ARGS)
 	amroutine->amvacuumcleanup = btvacuumcleanup;
 	amroutine->amcanreturn = btcanreturn;
 	amroutine->amcostestimate = btcostestimate;
-	amroutine->amoptions = btoptions;
 	amroutine->amproperty = btproperty;
 	amroutine->amvalidate = btvalidate;
 	amroutine->ambeginscan = btbeginscan;
@@ -159,6 +157,7 @@ bthandler(PG_FUNCTION_ARGS)
 	amroutine->amendscan = btendscan;
 	amroutine->ammarkpos = btmarkpos;
 	amroutine->amrestrpos = btrestrpos;
+	amroutine->amrelopt_catalog = btgetreloptcatalog;
 	amroutine->amestimateparallelscan = btestimateparallelscan;
 	amroutine->aminitparallelscan = btinitparallelscan;
 	amroutine->amparallelrescan = btparallelrescan;
@@ -1390,3 +1389,26 @@ btcanreturn(Relation index, int attno)
 {
 	return true;
 }
+
+
+static options_catalog *bt_relopt_catalog = NULL;
+
+static void *
+btgetreloptcatalog(void)
+{
+	if (!bt_relopt_catalog)
+	{
+		bt_relopt_catalog = allocateOptionsCatalog(NULL,
+												   sizeof(BTRelOptions), 1);
+
+		optionsCatalogAddItemInt(bt_relopt_catalog, "fillfactor",
+						   "Packs btree index pages only to this percentage",
+								 ShareUpdateExclusiveLock,		/* since it applies only
+																 * to later inserts */
+								 0,
+								 offsetof(BTRelOptions, fillfactor),
+								 BTREE_DEFAULT_FILLFACTOR,
+								 BTREE_MIN_FILLFACTOR, 100);
+	}
+	return bt_relopt_catalog;
+}
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index bf6c03c..5c6957a 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -344,8 +344,8 @@ _bt_pagestate(BTWriteState *wstate, uint32 level)
 	if (level > 0)
 		state->btps_full = (BLCKSZ * (100 - BTREE_NONLEAF_FILLFACTOR) / 100);
 	else
-		state->btps_full = RelationGetTargetPageFreeSpace(wstate->index,
-														  BTREE_DEFAULT_FILLFACTOR);
+		state->btps_full =  (BLCKSZ * (100 - BTGetFillFactor(wstate->index)) / 100);
+
 	/* no parent level, yet */
 	state->btps_next = NULL;
 
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 9b53aa3..7a3b060 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -18,7 +18,7 @@
 #include <time.h>
 
 #include "access/nbtree.h"
-#include "access/reloptions.h"
+#include "storage/lock.h"
 #include "access/relscan.h"
 #include "miscadmin.h"
 #include "utils/array.h"
@@ -2038,12 +2038,6 @@ BTreeShmemInit(void)
 		Assert(found);
 }
 
-bytea *
-btoptions(Datum reloptions, bool validate)
-{
-	return default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);
-}
-
 /*
  *	btproperty() -- Check boolean properties of indexes.
  *
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index bd5301f..e6a6e68 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -15,7 +15,7 @@
 
 #include "postgres.h"
 
-#include "access/reloptions.h"
+#include "access/options.h"
 #include "access/spgist_private.h"
 #include "access/transam.h"
 #include "access/xact.h"
@@ -59,7 +59,6 @@ spghandler(PG_FUNCTION_ARGS)
 	amroutine->amvacuumcleanup = spgvacuumcleanup;
 	amroutine->amcanreturn = spgcanreturn;
 	amroutine->amcostestimate = spgcostestimate;
-	amroutine->amoptions = spgoptions;
 	amroutine->amproperty = NULL;
 	amroutine->amvalidate = spgvalidate;
 	amroutine->ambeginscan = spgbeginscan;
@@ -69,6 +68,7 @@ spghandler(PG_FUNCTION_ARGS)
 	amroutine->amendscan = spgendscan;
 	amroutine->ammarkpos = NULL;
 	amroutine->amrestrpos = NULL;
+	amroutine->amrelopt_catalog = spggetreloptcatalog;
 	amroutine->amestimateparallelscan = NULL;
 	amroutine->aminitparallelscan = NULL;
 	amroutine->amparallelrescan = NULL;
@@ -384,8 +384,8 @@ SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
 	 * related to the ones already on it.  But fillfactor mustn't cause an
 	 * error for requests that would otherwise be legal.
 	 */
-	needSpace += RelationGetTargetPageFreeSpace(index,
-												SPGIST_DEFAULT_FILLFACTOR);
+	needSpace += (BLCKSZ * (100 - SpGistGetFillFactor(index)) / 100);
+
 	needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
 
 	/* Get the cache entry for this flags setting */
@@ -557,15 +557,6 @@ SpGistInitMetapage(Page page)
 }
 
 /*
- * reloptions processing for SPGiST
- */
-bytea *
-spgoptions(Datum reloptions, bool validate)
-{
-	return default_reloptions(reloptions, validate, RELOPT_KIND_SPGIST);
-}
-
-/*
  * Get the space needed to store a non-null datum of the indicated type.
  * Note the result is already rounded up to a MAXALIGN boundary.
  * Also, we follow the SPGiST convention that pass-by-val types are
@@ -931,3 +922,27 @@ SpGistPageAddNewItem(SpGistState *state, Page page, Item item, Size size,
 
 	return offnum;
 }
+
+
+
+static options_catalog *spgist_relopt_catalog = NULL;
+
+void *
+spggetreloptcatalog(void)
+{
+	if (!spgist_relopt_catalog)
+	{
+		spgist_relopt_catalog = allocateOptionsCatalog(NULL,
+												sizeof(SpGistRelOptions), 1);
+
+		optionsCatalogAddItemInt(spgist_relopt_catalog, "fillfactor",
+						  "Packs spgist index pages only to this percentage",
+								 ShareUpdateExclusiveLock,		/* since it applies only
+																 * to later inserts */
+								 0,
+								 offsetof(SpGistRelOptions, fillfactor),
+								 SPGIST_DEFAULT_FILLFACTOR,
+								 SPGIST_MIN_FILLFACTOR, 100);
+	}
+	return spgist_relopt_catalog;
+}
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 539ca79..dfb2fd4 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -36,7 +36,7 @@
 /* Potentially set by pg_upgrade_support functions */
 Oid			binary_upgrade_next_toast_pg_type_oid = InvalidOid;
 
-static void CheckAndCreateToastTable(Oid relOid, Datum reloptions,
+static bool CheckAndCreateToastTable(Oid relOid, Datum reloptions,
 						 LOCKMODE lockmode, bool check);
 static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 				   Datum reloptions, LOCKMODE lockmode, bool check);
@@ -67,23 +67,26 @@ NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
 	CheckAndCreateToastTable(relOid, reloptions, lockmode, false);
 }
 
-void
+bool
 NewRelationCreateToastTable(Oid relOid, Datum reloptions)
 {
-	CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, false);
+	return CheckAndCreateToastTable(relOid, reloptions,
+									AccessExclusiveLock, false);
 }
 
-static void
+static bool
 CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, bool check)
 {
 	Relation	rel;
+	bool		success;
 
 	rel = heap_open(relOid, lockmode);
 
 	/* create_toast_table does all the work */
-	(void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check);
+	success = create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check);
 
 	heap_close(rel, NoLock);
+	return success;
 }
 
 /*
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 4d77411..85c517a 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -89,6 +89,8 @@ create_ctas_internal(List *attrList, IntoClause *into)
 	Datum		toast_options;
 	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	ObjectAddress intoRelationAddr;
+	List	   *toastDefList;
+	bool		toast_created;
 
 	/* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
 	is_matview = (into->viewQuery != NULL);
@@ -122,15 +124,20 @@ create_ctas_internal(List *attrList, IntoClause *into)
 	CommandCounterIncrement();
 
 	/* parse and validate reloptions for the toast table */
-	toast_options = transformRelOptions((Datum) 0,
-										create->options,
-										"toast",
-										validnsps,
-										true, false);
 
-	(void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
 
-	NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options);
+	optionsDefListValdateNamespaces(create->options, validnsps);
+	toastDefList = optionsDefListFilterNamespaces(create->options, "toast");
+
+	toast_options = transformOptions(get_toast_relopt_catalog(), (Datum) 0,
+									 toastDefList, 0);
+
+	toast_created = NewRelationCreateToastTable(intoRelationAddr.objectId,
+												toast_options);
+	if (!toast_created && toast_options)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("no TOAST relation was created for a table. Can't set toast.* storage parameters")));
 
 	/* Create the "view" part of a materialized view. */
 	if (is_matview)
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 9ad9915..6f78d7b 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -111,7 +111,7 @@ transformGenericOptions(Oid catalogId,
 						List *options,
 						Oid fdwvalidator)
 {
-	List	   *resultOptions = untransformRelOptions(oldOptions);
+	List	   *resultOptions = optionsTextArrayToDefList(oldOptions);
 	ListCell   *optcell;
 	Datum		result;
 
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 97091dd..ee94aec 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -329,7 +329,7 @@ DefineIndex(Oid relationId,
 	Form_pg_am	accessMethodForm;
 	IndexAmRoutine *amRoutine;
 	bool		amcanorder;
-	amoptions_function amoptions;
+	amrelopt_catalog_function amoption_catalog;
 	Datum		reloptions;
 	int16	   *coloptions;
 	IndexInfo  *indexInfo;
@@ -537,7 +537,7 @@ DefineIndex(Oid relationId,
 						accessMethodName)));
 
 	amcanorder = amRoutine->amcanorder;
-	amoptions = amRoutine->amoptions;
+	amoption_catalog = amRoutine->amrelopt_catalog;
 
 	pfree(amRoutine);
 	ReleaseSysCache(tuple);
@@ -551,10 +551,18 @@ DefineIndex(Oid relationId,
 	/*
 	 * Parse AM-specific options, convert to text array form, validate.
 	 */
-	reloptions = transformRelOptions((Datum) 0, stmt->options,
-									 NULL, NULL, false, false);
-
-	(void) index_reloptions(amoptions, reloptions, true);
+	if (amoption_catalog)
+	{
+		reloptions = transformOptions(amoption_catalog(),
+									  (Datum) 0, stmt->options, 0);
+	}
+	else
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("access method %s does not support options",
+						accessMethodName)));
+	}
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d19846d..65c33e9 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -520,7 +520,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	Datum		reloptions;
 	ListCell   *listptr;
 	AttrNumber	attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	Oid			ofTypeId;
 	ObjectAddress address;
 
@@ -606,13 +605,23 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
-	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
-									 true, false);
-
 	if (relkind == RELKIND_VIEW)
-		(void) view_reloptions(reloptions, true);
+	{
+		reloptions = transformOptions(
+									  get_view_relopt_catalog(),
+									  (Datum) 0, stmt->options, 0);
+	}
 	else
-		(void) heap_reloptions(relkind, reloptions, true);
+	{
+		/* If it is not view, then it if heap */
+		char	   *namespaces[] = HEAP_RELOPT_NAMESPACES;
+		List	   *heapDefList;
+
+		optionsDefListValdateNamespaces(stmt->options, namespaces);
+		heapDefList = optionsDefListFilterNamespaces(stmt->options, NULL);
+		reloptions = transformOptions(get_heap_relopt_catalog(),
+									  (Datum) 0, heapDefList, 0);
+	}
 
 	if (stmt->ofTypename)
 	{
@@ -3166,7 +3175,8 @@ void
 AlterTableInternal(Oid relid, List *cmds, bool recurse)
 {
 	Relation	rel;
-	LOCKMODE	lockmode = AlterTableGetLockLevel(cmds);
+
+	LOCKMODE	lockmode = AlterTableGetLockLevel(relid, cmds);
 
 	rel = relation_open(relid, lockmode);
 
@@ -3208,13 +3218,15 @@ AlterTableInternal(Oid relid, List *cmds, bool recurse)
  * otherwise we might end up with an inconsistent dump that can't restore.
  */
 LOCKMODE
-AlterTableGetLockLevel(List *cmds)
+
+AlterTableGetLockLevel(Oid relid, List *cmds)
 {
 	/*
 	 * This only works if we read catalog tables using MVCC snapshots.
 	 */
 	ListCell   *lcmd;
 	LOCKMODE	lockmode = ShareUpdateExclusiveLock;
+	Relation	rel;
 
 	foreach(lcmd, cmds)
 	{
@@ -3426,7 +3438,10 @@ AlterTableGetLockLevel(List *cmds)
 									 * getTables() */
 			case AT_ResetRelOptions:	/* Uses MVCC in getIndexes() and
 										 * getTables() */
-				cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
+				rel = relation_open(relid, NoLock);
+				cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+													castNode(List, cmd->def));
+				relation_close(rel,NoLock);
 				break;
 
 			case AT_AttachPartition:
@@ -4210,7 +4225,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
 						 errmsg("cannot rewrite system relation \"%s\"",
 								RelationGetRelationName(OldHeap))));
 
-			if (RelationIsUsedAsCatalogTable(OldHeap))
+			if (HeapIsUsedAsCatalogTable(OldHeap))
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 						 errmsg("cannot rewrite table \"%s\" used as a catalog table",
@@ -6349,11 +6364,11 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options,
 	/* Generate new proposed attoptions (text array) */
 	datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
 							&isnull);
-	newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
-									 castNode(List, options), NULL, NULL,
-									 false, isReset);
-	/* Validate new options */
-	(void) attribute_reloptions(newOptions, true);
+
+	newOptions = transformOptions(get_attribute_options_catalog(),
+								  isnull ? (Datum) 0 : datum,
+					  castNode(List, options), OPTIONS_PARSE_MODE_FOR_ALTER |
+							   (isReset ? OPTIONS_PARSE_MODE_FOR_RESET : 0));
 
 	/* Build new tuple. */
 	memset(repl_null, false, sizeof(repl_null));
@@ -10439,7 +10454,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 	Datum		repl_val[Natts_pg_class];
 	bool		repl_null[Natts_pg_class];
 	bool		repl_repl[Natts_pg_class];
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+	options_parse_mode parse_mode;
 
 	if (defList == NIL && operation != AT_ReplaceRelOptions)
 		return;					/* nothing to do */
@@ -10468,25 +10483,52 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 								&isnull);
 	}
 
-	/* Generate new proposed reloptions (text array) */
-	newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
-									 defList, NULL, validnsps, false,
-									 operation == AT_ResetRelOptions);
-
 	/* Validate */
+	parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;
+	if (operation == AT_ResetRelOptions)
+		parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;
+
 	switch (rel->rd_rel->relkind)
 	{
 		case RELKIND_RELATION:
 		case RELKIND_TOASTVALUE:
 		case RELKIND_MATVIEW:
 		case RELKIND_PARTITIONED_TABLE:
-			(void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
+			{
+				char	   *namespaces[] = HEAP_RELOPT_NAMESPACES;
+				List	   *heapDefList;
+
+				optionsDefListValdateNamespaces(defList, namespaces);
+				heapDefList = optionsDefListFilterNamespaces(
+															 defList, NULL);
+				newOptions = transformOptions(get_heap_relopt_catalog(),
+											  isnull ? (Datum) 0 : datum,
+											  heapDefList, parse_mode);
+			}
 			break;
 		case RELKIND_VIEW:
-			(void) view_reloptions(newOptions, true);
+			{
+				newOptions = transformOptions(
+											  get_view_relopt_catalog(),
+											  isnull ? (Datum) 0 : datum,
+											  defList, parse_mode);
+			}
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			if (rel->rd_amroutine->amrelopt_catalog)
+			{
+				newOptions = transformOptions(
+									   rel->rd_amroutine->amrelopt_catalog(),
+											  isnull ? (Datum) 0 : datum,
+											  defList, parse_mode);
+			}
+			else
+			{
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("index %s does not support options",
+								RelationGetRelationName(rel))));
+			}
 			break;
 		default:
 			ereport(ERROR,
@@ -10500,7 +10542,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 	if (rel->rd_rel->relkind == RELKIND_VIEW)
 	{
 		Query	   *view_query = get_view_query(rel);
-		List	   *view_options = untransformRelOptions(newOptions);
+		List	   *view_options = optionsTextArrayToDefList(newOptions);
 		ListCell   *cell;
 		bool		check_option = false;
 
@@ -10560,6 +10602,8 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 	{
 		Relation	toastrel;
 		Oid			toastid = rel->rd_rel->reltoastrelid;
+		List	   *toastDefList;
+		options_parse_mode parse_mode;
 
 		toastrel = heap_open(toastid, lockmode);
 
@@ -10583,12 +10627,15 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
 									&isnull);
 		}
+		parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;
+		if (operation == AT_ResetRelOptions)
+			parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;
 
-		newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
-										 defList, "toast", validnsps, false,
-										 operation == AT_ResetRelOptions);
+		toastDefList = optionsDefListFilterNamespaces(defList, "toast");
 
-		(void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
+		newOptions = transformOptions(get_toast_relopt_catalog(),
+									  isnull ? (Datum) 0 : datum,
+									  toastDefList, parse_mode);
 
 		memset(repl_val, 0, sizeof(repl_val));
 		memset(repl_null, false, sizeof(repl_null));
@@ -10616,6 +10663,18 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 
 		heap_close(toastrel, NoLock);
 	}
+	else
+	{
+		List	   *toastDefList;
+
+		toastDefList = optionsDefListFilterNamespaces(defList, "toast");
+		if (toastDefList)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("TOAST relation for this table does not exist. Can't change toast.* storage parameters")));
+		}
+	}
 
 	heap_close(pgclass, RowExclusiveLock);
 }
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index d574e4d..19f4cbf 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -332,10 +332,11 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
 	nulls[Anum_pg_tablespace_spcacl - 1] = true;
 
 	/* Generate new proposed spcoptions (text array) */
-	newOptions = transformRelOptions((Datum) 0,
-									 stmt->options,
-									 NULL, NULL, false, false);
-	(void) tablespace_reloptions(newOptions, true);
+
+	newOptions = transformOptions(
+								  get_tablespace_options_catalog(),
+								  (Datum) 0, stmt->options, 0);
+
 	if (newOptions != (Datum) 0)
 		values[Anum_pg_tablespace_spcoptions - 1] = newOptions;
 	else
@@ -1023,10 +1024,13 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
 	/* Generate new proposed spcoptions (text array) */
 	datum = heap_getattr(tup, Anum_pg_tablespace_spcoptions,
 						 RelationGetDescr(rel), &isnull);
-	newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
-									 stmt->options, NULL, NULL, false,
-									 stmt->isReset);
-	(void) tablespace_reloptions(newOptions, true);
+
+
+	newOptions = transformOptions(get_tablespace_options_catalog(),
+								  isnull ? (Datum) 0 : datum,
+								  stmt->options,
+								  OPTIONS_PARSE_MODE_FOR_ALTER |
+						 (stmt->isReset ? OPTIONS_PARSE_MODE_FOR_RESET : 0));
 
 	/* Build new tuple. */
 	memset(repl_null, false, sizeof(repl_null));
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index 45fca52..05f0e2d 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -62,7 +62,7 @@ GetForeignDataWrapper(Oid fdwid)
 	if (isnull)
 		fdw->options = NIL;
 	else
-		fdw->options = untransformRelOptions(datum);
+		fdw->options = optionsTextArrayToDefList(datum);
 
 	ReleaseSysCache(tp);
 
@@ -133,7 +133,7 @@ GetForeignServer(Oid serverid)
 	if (isnull)
 		server->options = NIL;
 	else
-		server->options = untransformRelOptions(datum);
+		server->options = optionsTextArrayToDefList(datum);
 
 	ReleaseSysCache(tp);
 
@@ -201,7 +201,7 @@ GetUserMapping(Oid userid, Oid serverid)
 	if (isnull)
 		um->options = NIL;
 	else
-		um->options = untransformRelOptions(datum);
+		um->options = optionsTextArrayToDefList(datum);
 
 	ReleaseSysCache(tp);
 
@@ -238,7 +238,7 @@ GetForeignTable(Oid relid)
 	if (isnull)
 		ft->options = NIL;
 	else
-		ft->options = untransformRelOptions(datum);
+		ft->options = optionsTextArrayToDefList(datum);
 
 	ReleaseSysCache(tp);
 
@@ -271,7 +271,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
 	if (isnull)
 		options = NIL;
 	else
-		options = untransformRelOptions(datum);
+		options = optionsTextArrayToDefList(datum);
 
 	ReleaseSysCache(tp);
 
@@ -540,7 +540,7 @@ pg_options_to_table(PG_FUNCTION_ARGS)
 	Datum		array = PG_GETARG_DATUM(0);
 
 	deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo,
-						  untransformRelOptions(array));
+						  optionsTextArrayToDefList(array));
 
 	return (Datum) 0;
 }
@@ -611,7 +611,7 @@ is_conninfo_option(const char *option, Oid context)
 Datum
 postgresql_fdw_validator(PG_FUNCTION_ARGS)
 {
-	List	   *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+	List	   *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
 	Oid			catalog = PG_GETARG_OID(1);
 
 	ListCell   *cell;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 9d35a41..871dccc 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -145,7 +145,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 						  &rel->pages, &rel->tuples, &rel->allvisfrac);
 
 	/* Retrieve the parallel_workers reloption, or -1 if not set. */
-	rel->rel_parallel_workers = RelationGetParallelWorkers(relation, -1);
+	rel->rel_parallel_workers = HeapGetParallelWorkers(relation, -1);
 
 	/*
 	 * Make list of indexes.  Ignore indexes on system catalogs if told to.
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 2828bbf..1be609c 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -3128,7 +3128,7 @@ transformOnConflictArbiter(ParseState *pstate,
 									exprLocation((Node *) onConflictClause))));
 
 	/* Same applies to table used by logical decoding as catalog table */
-	if (RelationIsUsedAsCatalogTable(pstate->p_target_relation))
+	if (HeapIsUsedAsCatalogTable(pstate->p_target_relation))
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("ON CONFLICT is not supported on table \"%s\" used as a catalog table",
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 8461da4..82ca4bb 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1524,7 +1524,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
 	datum = SysCacheGetAttr(RELOID, ht_idxrel,
 							Anum_pg_class_reloptions, &isnull);
 	if (!isnull)
-		index->options = untransformRelOptions(datum);
+		index->options = optionsTextArrayToDefList(datum);
 
 	/* If it's a partial index, decompile and append the predicate */
 	datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 48765bb..f9e03a3 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2704,6 +2704,7 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
 {
 	bytea	   *relopts;
 	AutoVacOpts *av;
+	AutoVacOpts *src;
 
 	Assert(((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_RELATION ||
 		   ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW ||
@@ -2713,8 +2714,13 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
 	if (relopts == NULL)
 		return NULL;
 
+	if (((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE)
+		src = &(((ToastOptions *) relopts)->autovacuum);
+	else
+		src = &(((HeapOptions *) relopts)->autovacuum);
+
 	av = palloc(sizeof(AutoVacOpts));
-	memcpy(av, &(((StdRdOptions *) relopts)->autovacuum), sizeof(AutoVacOpts));
+	memcpy(av, src, sizeof(AutoVacOpts));
 	pfree(relopts);
 
 	return av;
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index c2bc3ad..8ced76e 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1562,7 +1562,7 @@ ApplyRetrieveRule(Query *parsetree,
 
 	rte->rtekind = RTE_SUBQUERY;
 	rte->relid = InvalidOid;
-	rte->security_barrier = RelationIsSecurityView(relation);
+	rte->security_barrier = ViewIsSecurityView(relation);
 	rte->subquery = rule_action;
 	rte->inh = false;			/* must not be set for a subquery */
 
@@ -3066,7 +3066,7 @@ rewriteTargetView(Query *parsetree, Relation view)
 
 		ChangeVarNodes(viewqual, base_rt_index, new_rt_index, 0);
 
-		if (RelationIsSecurityView(view))
+		if (ViewIsSecurityView(view))
 		{
 			/*
 			 * The view's quals go in front of existing barrier quals: those
@@ -3102,8 +3102,9 @@ rewriteTargetView(Query *parsetree, Relation view)
 	 */
 	if (parsetree->commandType != CMD_DELETE)
 	{
-		bool		has_wco = RelationHasCheckOption(view);
-		bool		cascaded = RelationHasCascadedCheckOption(view);
+
+		bool		has_wco = ViewHasCheckOption(view);
+		bool		cascaded = ViewHasCascadedCheckOption(view);
 
 		/*
 		 * If the parent view has a cascaded check option, treat this view as
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 82a707a..9f1ca86 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1003,6 +1003,8 @@ ProcessUtilitySlow(ParseState *pstate,
 						{
 							Datum		toast_options;
 							static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+							List	   *toastDefList;
+							bool		success;
 
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
@@ -1023,18 +1025,24 @@ ProcessUtilitySlow(ParseState *pstate,
 							 * parse and validate reloptions for the toast
 							 * table
 							 */
-							toast_options = transformRelOptions((Datum) 0,
-																((CreateStmt *) stmt)->options,
-																"toast",
-																validnsps,
-																true,
-																false);
-							(void) heap_reloptions(RELKIND_TOASTVALUE,
-												   toast_options,
-												   true);
 
-							NewRelationCreateToastTable(address.objectId,
-														toast_options);
+							optionsDefListValdateNamespaces(
+											  ((CreateStmt *) stmt)->options,
+															validnsps);
+
+							toastDefList = optionsDefListFilterNamespaces(
+									((CreateStmt *) stmt)->options, "toast");
+
+							toast_options = transformOptions(
+									   get_toast_relopt_catalog(), (Datum) 0,
+															 toastDefList, 0);
+
+							success = NewRelationCreateToastTable(
+											address.objectId, toast_options);
+							if (!success && toast_options)
+								ereport(ERROR,
+								   (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+									errmsg("no TOAST relation was created for a table. Can't set toast.* storage parameters")));
 						}
 						else if (IsA(stmt, CreateForeignTableStmt))
 						{
@@ -1101,8 +1109,13 @@ ProcessUtilitySlow(ParseState *pstate,
 					 * lock on (for example) a relation on which we have no
 					 * permissions.
 					 */
-					lockmode = AlterTableGetLockLevel(atstmt->cmds);
-					relid = AlterTableLookupRelation(atstmt, lockmode);
+
+					relid = AlterTableLookupRelation(atstmt, NoLock);
+					if (OidIsValid(relid))
+					{
+						lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+						relid = AlterTableLookupRelation(atstmt, lockmode);
+					}
 
 					if (OidIsValid(relid))
 					{
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index da8b42d..e8c4309 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -149,7 +149,10 @@ get_attribute_options(Oid attrelid, int attnum)
 				opts = NULL;
 			else
 			{
-				bytea	   *bytea_opts = attribute_reloptions(datum, false);
+				bytea	   *bytea_opts;
+
+				bytea_opts = optionsTextArrayToBytea(
+									 get_attribute_options_catalog(), datum);
 
 				opts = MemoryContextAlloc(CacheMemoryContext,
 										  VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 1908420..a722f45 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -456,7 +456,8 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	options = extractRelOptions(tuple,
 								GetPgClassDescriptor(),
 								relation->rd_rel->relkind == RELKIND_INDEX ?
-								relation->rd_amroutine->amoptions : NULL);
+							  relation->rd_amroutine->amrelopt_catalog : NULL
+		);
 
 	/*
 	 * Copy parsed data into CacheMemoryContext.  To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index eafc00f..35db485 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,10 @@ get_tablespace(Oid spcid)
 			opts = NULL;
 		else
 		{
-			bytea	   *bytea_opts = tablespace_reloptions(datum, false);
+			bytea	   *bytea_opts;
+
+			bytea_opts = optionsTextArrayToBytea(
+									get_tablespace_options_catalog(), datum);
 
 			opts = MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts));
 			memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
@@ -194,7 +197,6 @@ get_tablespace_page_costs(Oid spcid,
 		else
 			*spc_random_page_cost = spc->opts->random_page_cost;
 	}
-
 	if (spc_seq_page_cost)
 	{
 		if (!spc->opts || spc->opts->seq_page_cost < 0)
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 0db4fc7..b226dc7 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -98,10 +98,6 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
 										 double *indexCorrelation,
 										 double *indexPages);
 
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
-									  bool validate);
-
 /* report AM, index, or index column property */
 typedef bool (*amproperty_function) (Oid index_oid, int attno,
 									 IndexAMProperty prop, const char *propname,
@@ -139,6 +135,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
 /* restore marked scan position */
 typedef void (*amrestrpos_function) (IndexScanDesc scan);
 
+/* get catalog of reloptions definitions */
+typedef void *(*amrelopt_catalog_function) ();
+
 /*
  * Callback function signatures - for parallel index scans.
  */
@@ -202,7 +201,6 @@ typedef struct IndexAmRoutine
 	amvacuumcleanup_function amvacuumcleanup;
 	amcanreturn_function amcanreturn;	/* can be NULL */
 	amcostestimate_function amcostestimate;
-	amoptions_function amoptions;
 	amproperty_function amproperty; /* can be NULL */
 	amvalidate_function amvalidate;
 	ambeginscan_function ambeginscan;
@@ -212,6 +210,7 @@ typedef struct IndexAmRoutine
 	amendscan_function amendscan;
 	ammarkpos_function ammarkpos;	/* can be NULL */
 	amrestrpos_function amrestrpos; /* can be NULL */
+	amrelopt_catalog_function amrelopt_catalog; /* can be NULL */
 
 	/* interface functions to support parallel index scans */
 	amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 61a3880..d65ad4e 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -37,6 +37,9 @@ typedef struct BrinStatsData
 
 
 #define BRIN_DEFAULT_PAGES_PER_RANGE	128
+#define BRIN_MIN_PAGES_PER_RANGE		1
+#define BRIN_MAX_PAGES_PER_RANGE		131072
+
 #define BrinGetPagesPerRange(relation) \
 	((relation)->rd_options ? \
 	 ((BrinOptions *) (relation)->rd_options)->pagesPerRange : \
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index 3ed6743..00eccac 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -102,7 +102,6 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
 			   void *callback_state);
 extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
 				  IndexBulkDeleteResult *stats);
-extern bytea *brinoptions(Datum reloptions, bool validate);
 
 /* brin_validate.c */
 extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index dc49b6f..4d91551 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -84,7 +84,7 @@ typedef struct GinState
 
 
 /* ginutil.c */
-extern bytea *ginoptions(Datum reloptions, bool validate);
+extern Datum ginhandler(PG_FUNCTION_ARGS);
 extern void initGinState(GinState *state, Relation index);
 extern Buffer GinNewBuffer(Relation index);
 extern void GinInitBuffer(Buffer b, uint32 f);
@@ -103,6 +103,7 @@ extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
 extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
 extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
 				 GinNullCategory *category);
+extern void *gingetreloptcatalog(void);
 
 /* gininsert.c */
 extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index eb1c672..6a9090e 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -367,6 +367,24 @@ typedef struct GISTBuildBuffers
 	int			rootlevel;
 } GISTBuildBuffers;
 
+
+/*
+ * Definition of items of enum type. Names and codes. To add or modify item
+ * edit both lists
+ */
+#define GIST_OPTION_BUFFERING_VALUE_NAMES { \
+	"on",									\
+	"off",									\
+	"auto",									\
+	(const char *) NULL						\
+}
+typedef enum gist_option_buffering_value_numbers
+{
+	GIST_OPTION_BUFFERING_ON = 0,
+	GIST_OPTION_BUFFERING_OFF = 1,
+	GIST_OPTION_BUFFERING_AUTO = 2,
+}	gist_option_buffering_value_numbers;
+
 /*
  * Storage type for GiST's reloptions
  */
@@ -374,7 +392,7 @@ typedef struct GiSTOptions
 {
 	int32		vl_len_;		/* varlena header (do not touch directly!) */
 	int			fillfactor;		/* page fill factor in percent (0..100) */
-	int			bufferingModeOffset;	/* use buffering build? */
+	int			buffering_mode; /* use buffering build? */
 } GiSTOptions;
 
 /* gist.c */
@@ -435,7 +453,6 @@ extern bool gistvalidate(Oid opclassoid);
 #define GIST_MIN_FILLFACTOR			10
 #define GIST_DEFAULT_FILLFACTOR		90
 
-extern bytea *gistoptions(Datum reloptions, bool validate);
 extern bool gistproperty(Oid index_oid, int attno,
 			 IndexAMProperty prop, const char *propname,
 			 bool *res, bool *isnull);
@@ -485,6 +502,7 @@ extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
 				 Datum *dst, bool *dstisnull);
 
 extern XLogRecPtr gistGetFakeLSN(Relation rel);
+extern void *gistgetreloptcatalog(void);
 
 /* gistvacuum.c */
 extern IndexBulkDeleteResult *gistbulkdelete(IndexVacuumInfo *info,
@@ -503,7 +521,6 @@ extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup,
 /* gistbuild.c */
 extern IndexBuildResult *gistbuild(Relation heap, Relation index,
 		  struct IndexInfo *indexInfo);
-extern void gistValidateBufferingOption(const char *value);
 
 /* gistbuildbuffers.c */
 extern GISTBuildBuffers *gistInitBuildBuffers(int pagesPerBuffer, int levelStep,
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index e3135c1..84f527b 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -271,6 +271,19 @@ typedef struct HashMetaPageData
 
 typedef HashMetaPageData *HashMetaPage;
 
+
+typedef struct HashRelOptions
+{
+	int32		varlena_header_;	/* varlena header (do not touch directly!) */
+	int			fillfactor;		/* page fill factor in percent (0..100) */
+}	HashRelOptions;
+
+
+#define HashGetFillFactor(relation) \
+	((relation)->rd_options ? \
+		((HashRelOptions *) (relation)->rd_options)->fillfactor : \
+		HASH_DEFAULT_FILLFACTOR)
+
 /*
  * Maximum size of a hash index item (it's okay to have only one per page)
  */
@@ -374,7 +387,6 @@ extern IndexBulkDeleteResult *hashbulkdelete(IndexVacuumInfo *info,
 			   void *callback_state);
 extern IndexBulkDeleteResult *hashvacuumcleanup(IndexVacuumInfo *info,
 				  IndexBulkDeleteResult *stats);
-extern bytea *hashoptions(Datum reloptions, bool validate);
 extern bool hashvalidate(Oid opclassoid);
 
 extern Datum hash_any(register const unsigned char *k, register int keylen);
@@ -479,4 +491,6 @@ extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
 				  bool bucket_has_garbage,
 				  IndexBulkDeleteCallback callback, void *callback_state);
 
+extern void *hashgetreloptcatalog(void);
+
 #endif							/* HASH_H */
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 2d4c36d..1479646 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -427,6 +427,17 @@ typedef BTScanOpaqueData *BTScanOpaque;
 #define SK_BT_DESC			(INDOPTION_DESC << SK_BT_INDOPTION_SHIFT)
 #define SK_BT_NULLS_FIRST	(INDOPTION_NULLS_FIRST << SK_BT_INDOPTION_SHIFT)
 
+typedef struct BTRelOptions
+{
+	int32		varlena_header_;	/* varlena header (do not touch directly!) */
+	int			fillfactor;		/* page fill factor in percent (0..100) */
+}	BTRelOptions;
+
+#define BTGetFillFactor(relation) \
+	((relation)->rd_options ? \
+		((BTRelOptions *) (relation)->rd_options)->fillfactor : \
+		BTREE_DEFAULT_FILLFACTOR)
+
 /*
  * external entry points for btree, in nbtree.c
  */
@@ -534,7 +545,6 @@ extern void _bt_end_vacuum(Relation rel);
 extern void _bt_end_vacuum_callback(int code, Datum arg);
 extern Size BTreeShmemSize(void);
 extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
 extern bool btproperty(Oid index_oid, int attno,
 		   IndexAMProperty prop, const char *propname,
 		   bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 0000000..239d192
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,225 @@
+/*-------------------------------------------------------------------------
+ *
+ * options.h
+ *	  Core support for relation and tablespace options (pg_class.reloptions
+ *	  and pg_tablespace.spcoptions)
+ *
+ * Note: the functions dealing with text-array options values declare
+ * them as Datum, not ArrayType *, to avoid needing to include array.h
+ * into a lot of low-level code.
+ *
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/options.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+#include "storage/lock.h"
+#include "nodes/pg_list.h"
+
+/* types supported by reloptions */
+typedef enum option_type
+{
+	OPTION_TYPE_BOOL,
+	OPTION_TYPE_INT,
+	OPTION_TYPE_REAL,
+	OPTION_TYPE_ENUM,
+	OPTION_TYPE_STRING
+}	option_type;
+
+typedef enum option_value_status
+{
+	OPTION_VALUE_STATUS_EMPTY,	/* Option was just initialized */
+	OPTION_VALUE_STATUS_RAW,	/* Option just came from syntax analyzer in
+								 * has name, and raw (unparsed) value */
+	OPTION_VALUE_STATUS_PARSED, /* Option was parsed and has link to catalog
+								 * entry and proper value */
+	OPTION_VALUE_STATUS_FOR_RESET		/* This option came from ALTER xxx
+										 * RESET */
+}	option_value_status;
+
+/* flags for reloptinon definition */
+typedef enum option_definition_flags
+{
+	OPTION_DEFINITION_FLAG_FORBID_ALTER = (1 << 0),		/* Altering this option
+														 * is forbidden */
+	OPTION_DEFINITION_FLAG_IGNORE = (1 << 1),	/* Skip this option while
+												 * parsing. Used for WITH OIDS
+												 * special case */
+	OPTION_DEFINITION_FLAG_REJECT = (1 << 2)	/* Option will be rejected
+												 * when comes from syntax
+												 * analyzer, but still have
+												 * default value and offset */
+} option_definition_flags;
+
+/* flags that tells reloption parser how to parse*/
+typedef enum options_parse_mode
+{
+	OPTIONS_PARSE_MODE_VALIDATE = (1 << 0),
+	OPTIONS_PARSE_MODE_FOR_ALTER = (1 << 1),
+	OPTIONS_PARSE_MODE_FOR_RESET = (1 << 2)
+} options_parse_mode;
+
+
+/* generic struct to hold shared data */
+typedef struct option_definition_basic
+{
+	const char *name;			/* must be first (used as list termination
+								 * marker) */
+	const char *desc;
+	LOCKMODE	lockmode;
+	option_definition_flags flags;
+	option_type type;
+	int			struct_offset;	/* offset of the value in Bytea representation */
+}	option_definition_basic;
+
+/* holds a parsed value */
+typedef struct option_value
+{
+	option_definition_basic *gen;
+	char	   *namespace;
+	option_value_status status;
+	char	   *raw_value;		/* allocated separately */
+	char	   *raw_name;
+	union
+	{
+		bool		bool_val;
+		int			int_val;
+		double		real_val;
+		int			enum_val;
+		char	   *string_val; /* allocated separately */
+	}			values;
+}	option_value;
+
+/* reloptions records for specific variable types */
+typedef struct option_definition_bool
+{
+	option_definition_basic base;
+	bool		default_val;
+}	option_definition_bool;
+
+typedef struct option_definition_int
+{
+	option_definition_basic base;
+	int			default_val;
+	int			min;
+	int			max;
+}	option_definition_int;
+
+typedef struct option_definition_real
+{
+	option_definition_basic base;
+	double		default_val;
+	double		min;
+	double		max;
+}	option_definition_real;
+
+typedef struct option_definition_enum
+{
+	option_definition_basic base;
+	const char **allowed_values;/* Null terminated array of allowed values for
+								 * the option */
+	int			default_val;	/* Number of item of allowed_values array */
+}	option_definition_enum;
+
+/* validation routines for strings */
+typedef void (*validate_string_option) (const char *value);
+
+/*
+ * When storing sting reloptions, we shoud deal with special case when
+ * option value is not set. For fixed length options, we just copy default
+ * option value into the binary structure. For varlen value, there can be
+ * "not set" special case, with no default value offered.
+ * In this case we will set offset value to -1, so code that use relptions
+ * can deal this case. For better readability it was defined as a constant.
+ */
+#define OPTION_STRING_VALUE_NOT_SET_OFFSET -1
+
+typedef struct option_definition_string
+{
+	option_definition_basic base;
+	validate_string_option validate_cb;
+	char	   *default_val;
+}	option_definition_string;
+
+typedef void (*postprocess_bytea_options_function) (void *data, bool validate);
+
+typedef struct options_catalog
+{
+	option_definition_basic **definitions;
+	int			num;			/* Number of catalog items in use */
+	int			num_allocated;	/* Number of catalog items allocated */
+	bool		forbid_realloc; /* If number of items of the catalog were
+								 * strictly set to certain value do no allow
+								 * adding more idems */
+	Size		struct_size;	/* Size of a structure for options in binary
+								 * representation */
+	postprocess_bytea_options_function postprocess_fun; /* This function is
+														 * called after options
+														 * were converted in
+														 * Bytea represenation.
+														 * Can be used for extra
+														 * validation and so on */
+	char	   *namespace;		/* Catalog is used for options from this
+								 * namespase */
+}	options_catalog;
+
+
+/*
+ * Options catalog related functions
+ */
+extern options_catalog *allocateOptionsCatalog(const char *namespace, int size_of_bytea,
+					   int num_items_expected);
+extern void optionsCatalogAddItemBool(options_catalog * catalog, const char *name,
+		  const char *desc, LOCKMODE lockmode, option_definition_flags flags,
+						  int struct_offset, bool default_val);
+extern void optionsCatalogAddItemInt(options_catalog * catalog, const char *name,
+		  const char *desc, LOCKMODE lockmode, option_definition_flags flags,
+			   int struct_offset, int default_val, int min_val, int max_val);
+extern void optionsCatalogAddItemReal(options_catalog * catalog, const char *name,
+		  const char *desc, LOCKMODE lockmode, option_definition_flags flags,
+	  int struct_offset, double default_val, double min_val, double max_val);
+extern void optionsCatalogAddItemEnum(options_catalog * catalog,
+						  const char *name, const char *desc, LOCKMODE lockmode, option_definition_flags flags,
+			int struct_offset, const char **allowed_values, int default_val);
+extern void optionsCatalogAddItemString(options_catalog * catalog, const char *name,
+		  const char *desc, LOCKMODE lockmode, option_definition_flags flags,
+int struct_offset, const char *default_val, validate_string_option validator);
+
+
+/*
+ * This macro allows to get string option value from bytea representation.
+ * "optstruct" - is a structure that is stored in bytea options representation
+ * "member" - member of this structure that has string option value
+ * (actually string values are stored in bytea after the structure, and
+ * and "member" will contain an offset to this value. This macro do all
+ * the math
+ */
+#define GET_STRING_OPTION(optstruct, member) \
+	((optstruct)->member == OPTION_STRING_VALUE_NOT_SET_OFFSET ? NULL : \
+	 (char *)(optstruct) + (optstruct)->member)
+
+/*
+ * Functions related to option convertation, parsing, manipulation
+ * and validation
+ */
+extern void optionsDefListValdateNamespaces(List *defList,
+								char **allowed_namespaces);
+extern List *optionsDefListFilterNamespaces(List *defList, const char *namespace);
+extern List *optionsTextArrayToDefList(Datum options);
+
+/*
+ * Meta functions that uses functions above to get options for relations,
+ * tablespaces, views and so on
+ */
+
+extern bytea *optionsTextArrayToBytea(options_catalog * catalog, Datum data);
+extern Datum transformOptions(options_catalog * catalog, Datum oldOptions,
+				 List *defList, options_parse_mode parse_mode);
+
+#endif   /* OPTIONS_H */
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index cd43e3a..396263f 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -1,13 +1,9 @@
 /*-------------------------------------------------------------------------
  *
  * reloptions.h
- *	  Core support for relation and tablespace options (pg_class.reloptions
+ *	  Support for relation and tablespace options (pg_class.reloptions
  *	  and pg_tablespace.spcoptions)
  *
- * Note: the functions dealing with text-array reloptions values declare
- * them as Datum, not ArrayType *, to avoid needing to include array.h
- * into a lot of low-level code.
- *
  *
  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
@@ -20,264 +16,27 @@
 #define RELOPTIONS_H
 
 #include "access/amapi.h"
-#include "access/htup.h"
-#include "access/tupdesc.h"
-#include "nodes/pg_list.h"
-#include "storage/lock.h"
-
-/* types supported by reloptions */
-typedef enum relopt_type
-{
-	RELOPT_TYPE_BOOL,
-	RELOPT_TYPE_INT,
-	RELOPT_TYPE_REAL,
-	RELOPT_TYPE_STRING
-} relopt_type;
+#include "access/options.h"
 
-/* kinds supported by reloptions */
-typedef enum relopt_kind
-{
-	RELOPT_KIND_HEAP = (1 << 0),
-	RELOPT_KIND_TOAST = (1 << 1),
-	RELOPT_KIND_BTREE = (1 << 2),
-	RELOPT_KIND_HASH = (1 << 3),
-	RELOPT_KIND_GIN = (1 << 4),
-	RELOPT_KIND_GIST = (1 << 5),
-	RELOPT_KIND_ATTRIBUTE = (1 << 6),
-	RELOPT_KIND_TABLESPACE = (1 << 7),
-	RELOPT_KIND_SPGIST = (1 << 8),
-	RELOPT_KIND_VIEW = (1 << 9),
-	RELOPT_KIND_BRIN = (1 << 10),
-	RELOPT_KIND_PARTITIONED = (1 << 11),
-	/* if you add a new kind, make sure you update "last_default" too */
-	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_PARTITIONED,
-	/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
-	RELOPT_KIND_MAX = (1 << 30)
-} relopt_kind;
 
 /* reloption namespaces allowed for heaps -- currently only TOAST */
 #define HEAP_RELOPT_NAMESPACES { "toast", NULL }
 
-/* generic struct to hold shared data */
-typedef struct relopt_gen
-{
-	const char *name;			/* must be first (used as list termination
-								 * marker) */
-	const char *desc;
-	bits32		kinds;
-	LOCKMODE	lockmode;
-	int			namelen;
-	relopt_type type;
-} relopt_gen;
-
-/* holds a parsed value */
-typedef struct relopt_value
-{
-	relopt_gen *gen;
-	bool		isset;
-	union
-	{
-		bool		bool_val;
-		int			int_val;
-		double		real_val;
-		char	   *string_val; /* allocated separately */
-	}			values;
-} relopt_value;
-
-/* reloptions records for specific variable types */
-typedef struct relopt_bool
-{
-	relopt_gen	gen;
-	bool		default_val;
-} relopt_bool;
-
-typedef struct relopt_int
-{
-	relopt_gen	gen;
-	int			default_val;
-	int			min;
-	int			max;
-} relopt_int;
-
-typedef struct relopt_real
-{
-	relopt_gen	gen;
-	double		default_val;
-	double		min;
-	double		max;
-} relopt_real;
-
-/* validation routines for strings */
-typedef void (*validate_string_relopt) (const char *value);
-
-typedef struct relopt_string
-{
-	relopt_gen	gen;
-	int			default_len;
-	bool		default_isnull;
-	validate_string_relopt validate_cb;
-	char	   *default_val;
-} relopt_string;
-
-/* This is the table datatype for fillRelOptions */
-typedef struct
-{
-	const char *optname;		/* option's name */
-	relopt_type opttype;		/* option's datatype */
-	int			offset;			/* offset of field in result struct */
-} relopt_parse_elt;
-
-
-/*
- * These macros exist for the convenience of amoptions writers (but consider
- * using fillRelOptions, which is a lot simpler).  Beware of multiple
- * evaluation of arguments!
- *
- * The last argument in the HANDLE_*_RELOPTION macros allows the caller to
- * determine whether the option was set (true), or its value acquired from
- * defaults (false); it can be passed as (char *) NULL if the caller does not
- * need this information.
- *
- * optname is the option name (a string), var is the variable
- * on which the value should be stored (e.g. StdRdOptions->fillfactor), and
- * option is a relopt_value pointer.
- *
- * The normal way to use this is to loop on the relopt_value array returned by
- * parseRelOptions:
- * for (i = 0; options[i].gen->name; i++)
- * {
- *		if (HAVE_RELOPTION("fillfactor", options[i])
- *		{
- *			HANDLE_INT_RELOPTION("fillfactor", rdopts->fillfactor, options[i], &isset);
- *			continue;
- *		}
- *		if (HAVE_RELOPTION("default_row_acl", options[i])
- *		{
- *			...
- *		}
- *		...
- *		if (validate)
- *			ereport(ERROR,
- *					(errmsg("unknown option")));
- *	}
- *
- *	Note that this is more or less the same that fillRelOptions does, so only
- *	use this if you need to do something non-standard within some option's
- *	code block.
- */
-#define HAVE_RELOPTION(optname, option) \
-	(pg_strncasecmp(option.gen->name, optname, option.gen->namelen + 1) == 0)
-
-#define HANDLE_INT_RELOPTION(optname, var, option, wasset)		\
-	do {														\
-		if (option.isset)										\
-			var = option.values.int_val;						\
-		else													\
-			var = ((relopt_int *) option.gen)->default_val;		\
-		(wasset) != NULL ? *(wasset) = option.isset : (dummyret)NULL; \
-	} while (0)
-
-#define HANDLE_BOOL_RELOPTION(optname, var, option, wasset)			\
-	do {															\
-		if (option.isset)										\
-			var = option.values.bool_val;						\
-		else													\
-			var = ((relopt_bool *) option.gen)->default_val;	\
-		(wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \
-	} while (0)
 
-#define HANDLE_REAL_RELOPTION(optname, var, option, wasset)		\
-	do {														\
-		if (option.isset)										\
-			var = option.values.real_val;						\
-		else													\
-			var = ((relopt_real *) option.gen)->default_val;	\
-		(wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \
-	} while (0)
-
-/*
- * Note that this assumes that the variable is already allocated at the tail of
- * reloptions structure (StdRdOptions or equivalent).
- *
- * "base" is a pointer to the reloptions structure, and "offset" is an integer
- * variable that must be initialized to sizeof(reloptions structure).  This
- * struct must have been allocated with enough space to hold any string option
- * present, including terminating \0 for every option.  SET_VARSIZE() must be
- * called on the struct with this offset as the second argument, after all the
- * string options have been processed.
- */
-#define HANDLE_STRING_RELOPTION(optname, var, option, base, offset, wasset) \
-	do {														\
-		relopt_string *optstring = (relopt_string *) option.gen;\
-		char *string_val;										\
-		if (option.isset)										\
-			string_val = option.values.string_val;				\
-		else if (!optstring->default_isnull)					\
-			string_val = optstring->default_val;				\
-		else													\
-			string_val = NULL;									\
-		(wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \
-		if (string_val == NULL)									\
-			var = 0;											\
-		else													\
-		{														\
-			strcpy(((char *)(base)) + (offset), string_val);	\
-			var = (offset);										\
-			(offset) += strlen(string_val) + 1;					\
-		}														\
-	} while (0)
-
-/*
- * For use during amoptions: get the strlen of a string option
- * (either default or the user defined value)
- */
-#define GET_STRING_RELOPTION_LEN(option) \
-	((option).isset ? strlen((option).values.string_val) : \
-	 ((relopt_string *) (option).gen)->default_len)
+extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
+				  amrelopt_catalog_function catalog_fun);
 
 /*
- * For use by code reading options already parsed: get a pointer to the string
- * value itself.  "optstruct" is the StdRdOption struct or equivalent, "member"
- * is the struct member corresponding to the string option
+ * Functions that creates option catalog instances for heap, toast, view
+ * and other object types that are part of the postgres core
  */
-#define GET_STRING_RELOPTION(optstruct, member) \
-	((optstruct)->member == 0 ? NULL : \
-	 (char *)(optstruct) + (optstruct)->member)
 
+extern options_catalog *get_heap_relopt_catalog(void);
+extern options_catalog *get_toast_relopt_catalog(void);
 
-extern relopt_kind add_reloption_kind(void);
-extern void add_bool_reloption(bits32 kinds, const char *name, const char *desc,
-				   bool default_val);
-extern void add_int_reloption(bits32 kinds, const char *name, const char *desc,
-				  int default_val, int min_val, int max_val);
-extern void add_real_reloption(bits32 kinds, const char *name, const char *desc,
-				   double default_val, double min_val, double max_val);
-extern void add_string_reloption(bits32 kinds, const char *name, const char *desc,
-					 const char *default_val, validate_string_relopt validator);
-
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
-					const char *namspace, char *validnsps[],
-					bool ignoreOids, bool isReset);
-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 bytea *default_reloptions(Datum reloptions, bool validate,
-				   relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
-				 bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+extern options_catalog *get_view_relopt_catalog(void);
+extern options_catalog *get_attribute_options_catalog(void);
+extern options_catalog *get_tablespace_options_catalog(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
 
 #endif							/* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index d1bc396..780e0d7 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -20,10 +20,6 @@
 #include "lib/stringinfo.h"
 
 
-/* reloption parameters */
-#define SPGIST_MIN_FILLFACTOR			10
-#define SPGIST_DEFAULT_FILLFACTOR		80
-
 /* SPGiST opclass support function numbers */
 #define SPGIST_CONFIG_PROC				1
 #define SPGIST_CHOOSE_PROC				2
@@ -181,9 +177,6 @@ typedef struct spgLeafConsistentOut
 } spgLeafConsistentOut;
 
 
-/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
-
 /* spginsert.c */
 extern IndexBuildResult *spgbuild(Relation heap, Relation index,
 		 struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index 1c4b321..f8c562a 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -21,6 +21,19 @@
 #include "utils/relcache.h"
 
 
+typedef struct SpGistRelOptions
+{
+	int32		varlena_header_;	/* varlena header (do not touch directly!) */
+	int			fillfactor;		/* page fill factor in percent (0..100) */
+}	SpGistRelOptions;
+
+
+#define SpGistGetFillFactor(relation) \
+	((relation)->rd_options ? \
+		((SpGistRelOptions *) (relation)->rd_options)->fillfactor : \
+		SPGIST_DEFAULT_FILLFACTOR)
+
+
 /* Page numbers of fixed-location pages */
 #define SPGIST_METAPAGE_BLKNO	 (0)	/* metapage */
 #define SPGIST_ROOT_BLKNO		 (1)	/* root for normal entries */
@@ -380,6 +393,11 @@ typedef SpGistDeadTupleData *SpGistDeadTuple;
 #define GBUF_REQ_NULLS(flags)	((flags) & GBUF_NULLS)
 
 /* spgutils.c */
+
+/* reloption parameters */
+#define SPGIST_MIN_FILLFACTOR			10
+#define SPGIST_DEFAULT_FILLFACTOR		80
+
 extern SpGistCache *spgGetCache(Relation index);
 extern void initSpGistState(SpGistState *state, Relation index);
 extern Buffer SpGistNewBuffer(Relation index);
@@ -407,6 +425,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page,
 					 Item item, Size size,
 					 OffsetNumber *startOffset,
 					 bool errorOK);
+extern void *spggetreloptcatalog(void);
 
 /* spgdoinsert.c */
 extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h
index f82a799..c649173 100644
--- a/src/include/catalog/toasting.h
+++ b/src/include/catalog/toasting.h
@@ -19,7 +19,7 @@
 /*
  * toasting.c prototypes
  */
-extern void NewRelationCreateToastTable(Oid relOid, Datum reloptions);
+extern bool NewRelationCreateToastTable(Oid relOid, Datum reloptions);
 extern void NewHeapCreateToastTable(Oid relOid, Datum reloptions,
 						LOCKMODE lockmode);
 extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index da3ff5d..dc704b1 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -32,7 +32,7 @@ extern Oid	AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
 
 extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt);
 
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
 
 extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
 
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 68fd6fb..ff4251f 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -273,7 +273,7 @@ typedef struct AutoVacOpts
 	float8		analyze_scale_factor;
 } AutoVacOpts;
 
-typedef struct StdRdOptions
+typedef struct HeapOptions
 {
 	int32		vl_len_;		/* varlena header (do not touch directly!) */
 	int			fillfactor;		/* page fill factor in percent (0..100) */
@@ -281,115 +281,124 @@ typedef struct StdRdOptions
 	AutoVacOpts autovacuum;		/* autovacuum-related options */
 	bool		user_catalog_table; /* use as an additional catalog relation */
 	int			parallel_workers;	/* max number of parallel workers */
-} StdRdOptions;
+} HeapOptions;
+
+typedef struct ToastOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	AutoVacOpts autovacuum;		/* autovacuum-related options */
+}	ToastOptions;
 
 #define HEAP_MIN_FILLFACTOR			10
 #define HEAP_DEFAULT_FILLFACTOR		100
 
+#define TOAST_DEFAULT_FILLFACTOR	100 /* Only default is actually used */
+
 /*
- * RelationGetToastTupleTarget
+ * HeapGetToastTupleTarget
  *		Returns the relation's toast_tuple_target.  Note multiple eval of argument!
  */
-#define RelationGetToastTupleTarget(relation, defaulttarg) \
+#define HeapGetToastTupleTarget(relation, defaulttarg) \
 	((relation)->rd_options ? \
-	 ((StdRdOptions *) (relation)->rd_options)->toast_tuple_target : (defaulttarg))
+	 ((HeapOptions *) (relation)->rd_options)->toast_tuple_target : (defaulttarg))
 
 /*
- * RelationGetFillFactor
+ * HeapGetFillFactor
  *		Returns the relation's fillfactor.  Note multiple eval of argument!
  */
-#define RelationGetFillFactor(relation, defaultff) \
+#define HeapGetFillFactor(relation, defaultff) \
 	((relation)->rd_options ? \
-	 ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff))
+	 ((HeapOptions *) (relation)->rd_options)->fillfactor : (defaultff))
 
 /*
- * RelationGetTargetPageUsage
- *		Returns the relation's desired space usage per page in bytes.
- */
-#define RelationGetTargetPageUsage(relation, defaultff) \
-	(BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100)
-
-/*
- * RelationGetTargetPageFreeSpace
+ * HeapGetTargetPageFreeSpace
  *		Returns the relation's desired freespace per page in bytes.
  */
-#define RelationGetTargetPageFreeSpace(relation, defaultff) \
-	(BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
+#define HeapGetTargetPageFreeSpace(relation, defaultff) \
+	(BLCKSZ * (100 - HeapGetFillFactor(relation, defaultff)) / 100)
 
 /*
- * RelationIsUsedAsCatalogTable
+ * HeapIsUsedAsCatalogTable
  *		Returns whether the relation should be treated as a catalog table
  *		from the pov of logical decoding.  Note multiple eval of argument!
  */
-#define RelationIsUsedAsCatalogTable(relation)	\
+#define HeapIsUsedAsCatalogTable(relation)	\
 	((relation)->rd_options && \
 	 ((relation)->rd_rel->relkind == RELKIND_RELATION || \
 	  (relation)->rd_rel->relkind == RELKIND_MATVIEW) ? \
-	 ((StdRdOptions *) (relation)->rd_options)->user_catalog_table : false)
+	 ((HeapOptions *) (relation)->rd_options)->user_catalog_table : false)
 
 /*
- * RelationGetParallelWorkers
+ * HeapGetParallelWorkers
  *		Returns the relation's parallel_workers reloption setting.
  *		Note multiple eval of argument!
  */
-#define RelationGetParallelWorkers(relation, defaultpw) \
+#define HeapGetParallelWorkers(relation, defaultpw) \
 	((relation)->rd_options ? \
-	 ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+	 ((HeapOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
 
+#define ToastGetTargetPageFreeSpace() \
+	(BLCKSZ * (100 - TOAST_DEFAULT_FILLFACTOR) / 100)
 
 /*
  * ViewOptions
  *		Contents of rd_options for views
  */
+
+/*
+ * Definition of items of enum type. Names and codes. To add or modify item
+ * edit both lists
+ */
+#define VIEW_OPTION_CHECK_OPTION_VALUE_NAMES {	\
+	"local",									\
+	"cascaded",									\
+	(const char *) NULL							\
+}
+
+typedef enum view_option_check_option_value_numbers
+{
+	VIEW_OPTION_CHECK_OPTION_NOT_SET = -1,
+	VIEW_OPTION_CHECK_OPTION_LOCAL = 0,
+	VIEW_OPTION_CHECK_OPTION_CASCADED = 1,
+}	view_option_check_option_value_numbers;
+
 typedef struct ViewOptions
 {
 	int32		vl_len_;		/* varlena header (do not touch directly!) */
 	bool		security_barrier;
-	int			check_option_offset;
+	int			check_option;
 } ViewOptions;
 
 /*
- * RelationIsSecurityView
- *		Returns whether the relation is security view, or not.  Note multiple
- *		eval of argument!
+ * ViewIsSecurityView
+ *		Returns whether the view is security view, or not. Note multiple eval
+ *		of argument!
  */
-#define RelationIsSecurityView(relation)	\
+#define ViewIsSecurityView(relation)		\
 	((relation)->rd_options ?				\
-	 ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
+		((ViewOptions *) (relation)->rd_options)->security_barrier : false)
 
 /*
- * RelationHasCheckOption
- *		Returns true if the relation is a view defined with either the local
- *		or the cascaded check option.  Note multiple eval of argument!
+ * ViewHasCheckOption
+ *		Returns true if view is defined with either the local or the cascaded
+ *		check option. Note multiple eval of argument!
  */
-#define RelationHasCheckOption(relation)									\
-	((relation)->rd_options &&												\
-	 ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0)
 
-/*
- * RelationHasLocalCheckOption
- *		Returns true if the relation is a view defined with the local check
- *		option.  Note multiple eval of argument!
- */
-#define RelationHasLocalCheckOption(relation)								\
-	((relation)->rd_options &&												\
-	 ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ?	\
-	 strcmp((char *) (relation)->rd_options +								\
-			((ViewOptions *) (relation)->rd_options)->check_option_offset,	\
-			"local") == 0 : false)
+#define ViewHasCheckOption(relation)										\
+((relation)->rd_options &&													\
+	((ViewOptions *) (relation)->rd_options)->check_option !=				\
+	VIEW_OPTION_CHECK_OPTION_NOT_SET)
 
 /*
- * RelationHasCascadedCheckOption
- *		Returns true if the relation is a view defined with the cascaded check
- *		option.  Note multiple eval of argument!
+ * ViewHasCascadedCheckOption
+ *		Returns true if the view is defined with the cascaded check option.
+ *		Note multiple eval of argument!
  */
-#define RelationHasCascadedCheckOption(relation)							\
-	((relation)->rd_options &&												\
-	 ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ?	\
-	 strcmp((char *) (relation)->rd_options +								\
-			((ViewOptions *) (relation)->rd_options)->check_option_offset,	\
-			"cascaded") == 0 : false)
 
+#define ViewHasCascadedCheckOption(relation)								\
+((relation)->rd_options &&													\
+	((ViewOptions *) (relation)->rd_options)->check_option ==				\
+	VIEW_OPTION_CHECK_OPTION_CASCADED)
 
 /*
  * RelationIsValid
@@ -568,7 +577,7 @@ typedef struct ViewOptions
 #define RelationIsAccessibleInLogicalDecoding(relation) \
 	(XLogLogicalInfoActive() && \
 	 RelationNeedsWAL(relation) && \
-	 (IsCatalogRelation(relation) || RelationIsUsedAsCatalogTable(relation)))
+	 (IsCatalogRelation(relation) || HeapIsUsedAsCatalogTable(relation)))
 
 /*
  * RelationIsLogicallyLogged
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 11f0baa..9a6e6d7 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2463,7 +2463,9 @@ ERROR:  unrecognized parameter "autovacuum_enabled"
 alter view my_locks set (autovacuum_enabled = false);
 ERROR:  unrecognized parameter "autovacuum_enabled"
 alter table my_locks reset (autovacuum_enabled);
+ERROR:  unrecognized parameter "autovacuum_enabled"
 alter view my_locks reset (autovacuum_enabled);
+ERROR:  unrecognized parameter "autovacuum_enabled"
 begin;
 alter view my_locks set (security_barrier=off);
 select * from my_locks order by 1;
diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.out
index ca80f00..6e9c4a2 100644
--- a/src/test/regress/expected/brin.out
+++ b/src/test/regress/expected/brin.out
@@ -507,3 +507,8 @@ EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE b = 1;
    Filter: (b = 1)
 (2 rows)
 
+-- Check that changing reloptions for brin index is not allowed
+ALTER INDEX brinidx SET (pages_per_range = 10);
+ERROR:  changing parameter "pages_per_range" is not allowed
+ALTER INDEX brinidx RESET (pages_per_range);
+ERROR:  changing parameter "pages_per_range" is not allowed
diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.out
index f5a2993..b7550b1 100644
--- a/src/test/regress/expected/gist.out
+++ b/src/test/regress/expected/gist.out
@@ -13,7 +13,7 @@ drop index gist_pointidx2, gist_pointidx3, gist_pointidx4;
 -- Make sure bad values are refused
 create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
 ERROR:  invalid value for "buffering" option
-DETAIL:  Valid values are "on", "off", and "auto".
+DETAIL:  Valid values are "on", "off", "auto".
 create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
 ERROR:  value 9 out of bounds for option "fillfactor"
 DETAIL:  Valid values are between "10" and "100".
@@ -27,6 +27,20 @@ insert into gist_point_tbl (id, p)
 select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
 -- To test vacuum, delete some entries from all over the index.
 delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+ERROR:  invalid value for "buffering" option
+DETAIL:  Valid values are "on", "off", "auto".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+ERROR:  value 9 out of bounds for option "fillfactor"
+DETAIL:  Valid values are between "10" and "100".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+ERROR:  value 101 out of bounds for option "fillfactor"
+DETAIL:  Valid values are between "10" and "100".
 -- And also delete some concentration of values. (GiST doesn't currently
 -- attempt to delete pages even when they become empty, but if it did, this
 -- would exercise it)
@@ -34,6 +48,7 @@ delete from gist_point_tbl where id < 10000;
 vacuum analyze gist_point_tbl;
 -- rebuild the index with a different fillfactor
 alter index gist_pointidx SET (fillfactor = 40);
+ERROR:  changing parameter "fillfactor" is not allowed
 reindex index gist_pointidx;
 --
 -- Test Index-only plans on GiST indexes
diff --git a/src/test/regress/expected/hash_index.out b/src/test/regress/expected/hash_index.out
index e23de21..cba7665 100644
--- a/src/test/regress/expected/hash_index.out
+++ b/src/test/regress/expected/hash_index.out
@@ -219,6 +219,7 @@ INSERT INTO hash_split_heap SELECT a/2 FROM generate_series(1, 25000) a;
 VACUUM hash_split_heap;
 -- Rebuild the index using a different fillfactor
 ALTER INDEX hash_split_index SET (fillfactor = 10);
+ERROR:  changing parameter "fillfactor" is not allowed
 REINDEX INDEX hash_split_index;
 -- Clean up.
 DROP TABLE hash_split_heap;
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
index c4107d5..231c049 100644
--- a/src/test/regress/expected/reloptions.out
+++ b/src/test/regress/expected/reloptions.out
@@ -77,7 +77,7 @@ SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
 ALTER TABLE reloptions_test RESET (autovacuum_enabled,
 	autovacuum_analyze_scale_factor);
 SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND
-reloptions IS NULL;
+	reloptions IS NULL;
  reloptions 
 ------------
  
@@ -86,7 +86,7 @@ reloptions IS NULL;
 -- RESET fails if a value is specified
 ALTER TABLE reloptions_test RESET (fillfactor=12);
 ERROR:  RESET must not include values for parameters
--- The OIDS option is not stored
+-- The OIDS option is not stored as reloption
 DROP TABLE reloptions_test;
 CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
 SELECT reloptions, relhasoids FROM pg_class WHERE oid = 'reloptions_test'::regclass;
@@ -123,7 +123,24 @@ SELECT reloptions FROM pg_class WHERE oid = :toast_oid;
 
 -- Fail on non-existent options in toast namespace
 CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42);
-ERROR:  unrecognized parameter "not_existing_option"
+ERROR:  unrecognized parameter "toast.not_existing_option"
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+ERROR:  no TOAST relation was created for a table. Can't set toast.* storage parameters
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ERROR:  TOAST relation for this table does not exist. Can't change toast.* storage parameters
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+ERROR:  TOAST relation for this table does not exist. Can't change toast.* storage parameters
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+ERROR:  unrecognized parameter "toast.autovacuum_analyze_scale_factor"
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+ERROR:  unrecognized parameter "toast.autovacuum_analyze_threshold"
 -- Mix TOAST & heap
 DROP TABLE reloptions_test;
 CREATE TABLE reloptions_test (s VARCHAR) WITH
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 2090a41..55b216c 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -1551,7 +1551,7 @@ SELECT * FROM base_tbl;
 
 ALTER VIEW rw_view1 SET (check_option=here); -- invalid
 ERROR:  invalid value for "check_option" option
-DETAIL:  Valid values are "local" and "cascaded".
+DETAIL:  Valid values are "local", "cascaded".
 ALTER VIEW rw_view1 SET (check_option=local);
 INSERT INTO rw_view2 VALUES (-20); -- should fail
 ERROR:  new row violates check option for view "rw_view1"
diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.sql
index 11f8fe9..3559ef6 100644
--- a/src/test/regress/sql/brin.sql
+++ b/src/test/regress/sql/brin.sql
@@ -460,3 +460,6 @@ VACUUM ANALYZE brin_test;
 EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE a = 1;
 -- Ensure brin index is not used when values are not correlated
 EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE b = 1;
+-- Check that changing reloptions for brin index is not allowed
+ALTER INDEX brinidx SET (pages_per_range = 10);
+ALTER INDEX brinidx RESET (pages_per_range);
diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.sql
index bae722f..9e01909 100644
--- a/src/test/regress/sql/gist.sql
+++ b/src/test/regress/sql/gist.sql
@@ -28,6 +28,15 @@ select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
 -- To test vacuum, delete some entries from all over the index.
 delete from gist_point_tbl where id % 2 = 1;
 
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+
 -- And also delete some concentration of values. (GiST doesn't currently
 -- attempt to delete pages even when they become empty, but if it did, this
 -- would exercise it)
diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql
index c9119fd..21a315b 100644
--- a/src/test/regress/sql/reloptions.sql
+++ b/src/test/regress/sql/reloptions.sql
@@ -47,12 +47,12 @@ SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
 ALTER TABLE reloptions_test RESET (autovacuum_enabled,
 	autovacuum_analyze_scale_factor);
 SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND
-reloptions IS NULL;
+	reloptions IS NULL;
 
 -- RESET fails if a value is specified
 ALTER TABLE reloptions_test RESET (fillfactor=12);
 
--- The OIDS option is not stored
+-- The OIDS option is not stored as reloption
 DROP TABLE reloptions_test;
 CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
 SELECT reloptions, relhasoids FROM pg_class WHERE oid = 'reloptions_test'::regclass;
@@ -75,6 +75,22 @@ SELECT reloptions FROM pg_class WHERE oid = :toast_oid;
 -- Fail on non-existent options in toast namespace
 CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42);
 
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+
 -- Mix TOAST & heap
 DROP TABLE reloptions_test;
 
