diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 0cfe49a..21e59df 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -204,7 +204,7 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
 			 void *callback_state);
 extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
 				IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern bytea *bloptions(Datum reloptions, bool validate, bool for_alter);
 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..93014af 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,27 @@ 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 +451,38 @@ BloomInitMetapage(Relation index)
 	UnlockReleaseBuffer(metaBuffer);
 }
 
-/*
- * Parse reloptions for bloom index, producing a BloomOptions struct.
- */
-bytea *
-bloptions(Datum reloptions, bool validate)
+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);
+		bl_relopt_catalog->struct_size = sizeof(BloomOptions);
+		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 cbc50f7..7e3bbc0 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -210,3 +210,37 @@ ORDER BY 1;
  text_ops | t
 (2 rows)
 
+-- reloptions test
+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;
+    reloptions     
+-------------------
+ {length=7,col1=4}
+(1 row)
+
+-- check for min and max values
+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 2227460..dbac21c 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -81,3 +81,23 @@ SELECT opcname, amvalidate(opc.oid)
 FROM pg_opclass opc JOIN pg_am am ON am.oid = opcmethod
 WHERE amname = 'bloom'
 ORDER BY 1;
+
+-- reloptions test
+
+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
+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 eba6f27..3d1c73f 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1948,7 +1948,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 735b794..b07399f 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 e24db56..be15189 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 b22563b..f69bcbd 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"
@@ -70,6 +70,7 @@ static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a,
 			 BrinTuple *b);
 static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy);
 
+void *bringetreloptcatalog (void);
 
 /*
  * BRIN handler function: return IndexAmRoutine with access method parameters
@@ -103,7 +104,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;
@@ -113,6 +113,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;
@@ -756,36 +757,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)}
-	};
-
-	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.
  */
@@ -1235,3 +1206,25 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy)
 	if (vacuum_fsm)
 		FreeSpaceMapVacuum(idxrel);
 }
+
+static options_catalog * brin_relopt_catalog = NULL;
+
+void *
+bringetreloptcatalog (void)
+{
+	if (! brin_relopt_catalog)
+	{
+		brin_relopt_catalog = allocateOptionsCatalog(NULL);
+		brin_relopt_catalog->struct_size = sizeof(BrinOptions);
+
+		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);
+	}
+	return brin_relopt_catalog;
+}
diff --git a/src/backend/access/common/Makefile b/src/backend/access/common/Makefile
index fb27944..c9fb063 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 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..3f2c183
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1389 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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, char *name,
+					char *desc, LOCKMODE lockmode,
+					option_definition_flags flags, int struct_offset);
+
+static void parse_one_option(option_value *option, char *text_str,
+					int text_len, bool validate);
+void *optionsAllocateBytea(options_catalog *catalog, List *options);
+
+
+/*
+ * 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.
+ */
+
+options_catalog *
+allocateOptionsCatalog(char *namespace)
+{
+	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;
+
+	catalog->definitions = NULL;
+	catalog->num = 0;
+	catalog->num_allocated = 0;
+	catalog->struct_size = -1; /* Will Assert if not properly changed */
+	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 + 1 >= catalog->num_allocated)
+	{
+		MemoryContext oldcxt;
+
+		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->definitions[catalog->num + 1] = NULL;
+	catalog->num++;
+}
+
+/*
+ * allocateOptionDefinition
+ *		Allocate a new option definition and initialize the type-agnostic
+ *		fields
+ */
+static option_definition_basic *
+allocateOptionDefinition(int type, char *name, 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, char *name, 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, char *name,
+		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, char *name, 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, char *name, 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, char *name, char *desc,
+		LOCKMODE lockmode, option_definition_flags flags, int struct_offset,
+		char *default_val, validate_string_relopt 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
+ */
+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.
+ */
+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++)
+		{
+			text	   *option_src = DatumGetTextP(options[i]);
+			option_value * option_dst;
+			char	   *text_str = VARDATA(option_src);
+			int			text_len = VARSIZE(option_src) - 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
+ */
+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, 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.
+ */
+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, 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.
+ */
+void *
+optionsAllocateBytea(options_catalog *catalog, List *options)
+{
+	Size		size;
+	int			i;
+	ListCell   *cell;
+	int			length;
+	void	   *res;
+
+	Assert(catalog->struct_size > 0); /* Make sure it was initialized */
+	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.
+ *
+ */
+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 c506491..fb1386f 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,920 +20,16 @@
 
 #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 "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[] =
-{
-	{
-		{
-			"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
-	},
-	{
-		{
-			"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, char *name, 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, char *name, 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, char *name, 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, char *name, 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, char *name, char *desc, 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, 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++)
-		{
-			text	   *oldoption = DatumGetTextP(oldoptions[i]);
-			char	   *text_str = VARDATA(oldoption);
-			int			text_len = VARSIZE(oldoption) - 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.
@@ -946,11 +45,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,
@@ -966,16 +66,19 @@ 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;
@@ -986,529 +89,293 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 			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.
+ * 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
+ */
+
+/*
+ * 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.
  *
- * If there are no options of the given kind, numrelopts is set to 0 and NULL
- * is returned.
+ * 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.
  *
- * 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.
+ * 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
  */
-relopt_value *
-parseRelOptions(Datum options, bool validate, relopt_kind kind,
-				int *numrelopts)
-{
-	relopt_value *reloptions;
-	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)
-	{
-		*numrelopts = 0;
-		return NULL;
-	}
-
-	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++)
-		{
-			text	   *optiontext = DatumGetTextP(optiondatums[i]);
-			char	   *text_str = VARDATA(optiontext);
-			int			text_len = VARSIZE(optiontext) - 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;
+void
+add_autovacuum_options(options_catalog *catalog, int base_offset,
+						bool for_toast)
+{
+	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);
 }
 
 /*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * get_heap_relopt_catalog
+ * 		Returns an options catalog for heap relation.
  */
-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;
+static options_catalog * heap_relopt_catalog = NULL;
 
-	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)
+options_catalog *
+get_heap_relopt_catalog(void)
+{
+	if (! heap_relopt_catalog)
 	{
-		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;
+		heap_relopt_catalog = allocateOptionsCatalog(NULL);
+		heap_relopt_catalog->struct_size = sizeof(HeapOptions);
 
-				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;
+		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);
 
-				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;
+		add_autovacuum_options(heap_relopt_catalog,
+							   offsetof(HeapOptions, autovacuum), false);
 
-				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;
+		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);
 	}
-
-	if (parsed)
-		option->isset = true;
-	if (!nofree)
-		pfree(value);
+	return heap_relopt_catalog;
 }
 
 /*
- * 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.
- *
- * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
- * equivalent).
+ * get_toast_relopt_catalog
+ * 		Returns an options catalog for toast relation.
  */
-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;
+static options_catalog * toast_relopt_catalog = NULL;
 
-	return palloc0(size);
-}
-
-/*
- * Given the result of parseRelOptions and a parsing table, fill in the
- * struct (previously allocated with allocateReloptStruct) with the parsed
- * values.
- *
- * 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.
- */
-void
-fillRelOptions(void *rdopts, Size basesize,
-			   relopt_value *options, int numoptions,
-			   bool validate,
-			   const relopt_parse_elt *elems, int numelems)
+options_catalog *
+get_toast_relopt_catalog(void)
 {
-	int			i;
-	int			offset = basesize;
-
-	for (i = 0; i < numoptions; i++)
+	if (! toast_relopt_catalog)
 	{
-		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;
+		toast_relopt_catalog = allocateOptionsCatalog("toast");
+		toast_relopt_catalog->struct_size = sizeof(ToastOptions);
 
-						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);
+		add_autovacuum_options(toast_relopt_catalog,
+							   offsetof(ToastOptions, autovacuum), true);
 	}
-	SET_VARSIZE(rdopts, offset);
+	return toast_relopt_catalog;
 }
 
-
 /*
- * Option parser for anything that uses StdRdOptions.
+ * get_view_relopt_catalog
+ * 		Returns an options catalog for view 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)},
-		{"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)}
-	};
-
-	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));
+static options_catalog * view_relopt_catalog = NULL;
 
-	pfree(options);
-
-	return (bytea *) rdopts;
-}
-
-/*
- * Option parser for views
- */
-bytea *
-view_reloptions(Datum reloptions, bool validate)
+options_catalog *
+get_view_relopt_catalog(void)
 {
-	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);
+	static const char *enum_names[] = VIEW_OPTION_CHECK_OPTION_VALUE_NAMES;
 
-	/* if none set, we're done */
-	if (numoptions == 0)
-		return NULL;
-
-	vopts = allocateReloptStruct(sizeof(ViewOptions), options, numoptions);
-
-	fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numoptions,
-				   validate, tab, lengthof(tab));
-
-	pfree(options);
-
-	return (bytea *) vopts;
-}
+	if (! view_relopt_catalog)
+	{
+		view_relopt_catalog = allocateOptionsCatalog(NULL);
+		view_relopt_catalog->struct_size = sizeof(ViewOptions);
 
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
-{
-	StdRdOptions *rdopts;
+		optionsCatalogAddItemBool(view_relopt_catalog, "security_barrier",
+					"View acts as a row security barrier",
+					AccessExclusiveLock,
+					0, offsetof(ViewOptions, security_barrier), false);
 
-	switch (relkind)
-	{
-		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:
-		case RELKIND_PARTITIONED_TABLE:
-			return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
-		default:
-			/* other relkinds are not supported */
-			return NULL;
+		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;
 }
 
-
 /*
- * Parse options for indexes.
- *
- *	amoptions	index AM's option parser function
- *	reloptions	options as text[] datum
- *	validate	error flag
+ * get_attribute_options_catalog
+ * 		Returns an options catalog for heap attributes
  */
-bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
-{
-	Assert(amoptions != NULL);
-
-	/* Assume function is strict */
-	if (!PointerIsValid(DatumGetPointer(reloptions)))
-		return NULL;
-
-	return amoptions(reloptions, validate);
-}
+static options_catalog * attribute_options_catalog = NULL;
 
-/*
- * Option parser for attribute reloptions
- */
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+options_catalog *
+get_attribute_options_catalog(void)
 {
-	relopt_value *options;
-	AttributeOpts *aopts;
-	int			numoptions;
-	static const relopt_parse_elt tab[] = {
-		{"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
-		{"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
-	};
-
-	options = parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE,
-							  &numoptions);
-
-	/* if none set, we're done */
-	if (numoptions == 0)
-		return NULL;
-
-	aopts = allocateReloptStruct(sizeof(AttributeOpts), options, numoptions);
-
-	fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
-				   validate, tab, lengthof(tab));
+	if (! attribute_options_catalog)
+	{
+		attribute_options_catalog = allocateOptionsCatalog(NULL);
+		attribute_options_catalog->struct_size = sizeof(AttributeOpts);
 
-	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;
+static options_catalog * tablespace_options_catalog = NULL;
 
-	tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);
+options_catalog *
+get_tablespace_options_catalog(void)
+{
+	if (! tablespace_options_catalog)
+	{
+		tablespace_options_catalog = allocateOptionsCatalog(NULL);
+		tablespace_options_catalog->struct_size = sizeof(TableSpaceOpts);
 
-	fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
-				   validate, tab, lengthof(tab));
+		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);
 
-	pfree(options);
+		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);
 
-	return (bytea *) tsopts;
+		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;
 }
 
 /*
@@ -1518,33 +385,51 @@ 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;
+	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;
+		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;
+		DefElem *def = (DefElem *) lfirst(cell);
 
-		for (i = 0; relOpts[i]; i++)
+		int 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 d03d59d..50ed21c 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;
@@ -593,35 +594,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
  *
@@ -698,3 +670,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);
+		gin_relopt_catalog->struct_size = sizeof(GinOptions);
+
+		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 6593771..0a2cd59 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;
@@ -1583,3 +1583,4 @@ gistvacuumpage(Relation rel, Page page, Buffer buffer)
 	 * the page.
 	 */
 }
+
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index f1f08bb..936d4d0 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -126,11 +126,9 @@ 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 +232,6 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 }
 
 /*
- * Validator for "buffering" reloption on GiST indexes. Allows "on", "off"
- * and "auto" values.
- */
-void
-gistValidateBufferingOption(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 cbdaec9..efb5133 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.
  */
@@ -812,34 +811,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.
  *
@@ -965,3 +936,32 @@ 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);
+		gist_relopt_catalog->struct_size = sizeof(GiSTOptions);
+
+		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 1f8a7f6..3eb60de 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -77,7 +77,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;
@@ -87,6 +86,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 c73929c..e7c9239 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -358,7 +358,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 c705531..478cb44 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"
@@ -219,12 +219,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
  */
@@ -446,3 +440,26 @@ _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
 
 	return new_bucket;
 }
+
+static options_catalog * hash_relopt_catalog = NULL;
+
+void *
+hashgetreloptcatalog(void)
+{
+	if (! hash_relopt_catalog)
+	{
+		hash_relopt_catalog = allocateOptionsCatalog(NULL);
+		hash_relopt_catalog->struct_size = sizeof(HashRelOptions);
+
+		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 bffc971..f1a339d 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2673,7 +2673,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 6529fe3..78ecc46 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -323,8 +323,11 @@ RelationGetBufferForTuple(Relation relation, Size len,
 						len, MaxHeapTupleSize)));
 
 	/* Compute desired extra freespace due to fillfactor option */
-	saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
-												   HEAP_DEFAULT_FILLFACTOR);
+	if (IsToastRelation(relation))
+		saveFreeSpace = ToastGetTargetPageFreeSpace();
+	else
+		saveFreeSpace = HeapGetTargetPageFreeSpace(relation,
+													   HEAP_DEFAULT_FILLFACTOR);
 
 	if (otherBuffer != InvalidBuffer)
 		otherBlock = BufferGetBlockNumber(otherBuffer);
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index d69a266..fef5e40 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -131,8 +131,12 @@ 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,
-											 HEAP_DEFAULT_FILLFACTOR);
+
+	if (IsToastRelation(relation))
+		minfree = ToastGetTargetPageFreeSpace();
+	else
+		minfree = HeapGetTargetPageFreeSpace(relation,
+												   HEAP_DEFAULT_FILLFACTOR);
 	minfree = Max(minfree, BLCKSZ / 10);
 
 	if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index c7b283c..ee859e7 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -665,7 +665,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/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 6dca810..6b12304 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -1433,8 +1433,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 775f2ff..9d1b8ac 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
 {
@@ -116,8 +116,7 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 			 BTCycleId cycleid);
 static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
 			 BlockNumber orig_blkno);
-
-
+void * btgetreloptcatalog (void);
 /*
  * Btree handler function: return IndexAmRoutine with access method parameters
  * and callbacks.
@@ -150,7 +149,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;
@@ -160,6 +158,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;
@@ -1391,3 +1390,26 @@ btcanreturn(Relation index, int attno)
 {
 	return true;
 }
+
+
+static options_catalog * bt_relopt_catalog = NULL;
+
+void *
+btgetreloptcatalog (void)
+{
+	if (! bt_relopt_catalog)
+	{
+		bt_relopt_catalog = allocateOptionsCatalog(NULL);
+		bt_relopt_catalog->struct_size = sizeof(BTRelOptions);
+
+		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 3d041c4..692c751 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 5b259a3..3efb05c 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 "access/options.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 e57ac49..b24178c 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;
@@ -372,8 +372,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 */
@@ -537,15 +537,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
@@ -911,3 +902,26 @@ 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);
+		spgist_relopt_catalog->struct_size = sizeof(SpGistRelOptions);
+
+		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 0e42316..838b357 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 646a884..5020ee1 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 d5d40e6..7aa4576 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 72bb06c..071bf64 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -325,7 +325,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;
@@ -528,7 +528,7 @@ DefineIndex(Oid relationId,
 			   accessMethodName)));
 
 	amcanorder = amRoutine->amcanorder;
-	amoptions = amRoutine->amoptions;
+	amoption_catalog = amRoutine->amrelopt_catalog;
 
 	pfree(amRoutine);
 	ReleaseSysCache(tuple);
@@ -542,10 +542,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 1ddb72d..f6de506 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -499,7 +499,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	Datum		reloptions;
 	ListCell   *listptr;
 	AttrNumber	attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	Oid			ofTypeId;
 	ObjectAddress address;
 
@@ -585,13 +584,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)
 	{
@@ -3073,7 +3082,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);
 
@@ -3115,13 +3125,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)
 	{
@@ -3330,7 +3342,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:
@@ -4087,7 +4102,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",
@@ -5930,11 +5945,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));
@@ -9929,7 +9944,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 */
@@ -9958,25 +9973,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,
@@ -9990,7 +10032,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;
 
@@ -10050,6 +10092,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);
 
@@ -10073,12 +10117,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));
@@ -10106,6 +10153,17 @@ 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 f9c2620..9945318 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 fdb4f71..a32d376 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 463f806..a6b9570 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -135,7 +135,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 47ca685..d4e2281 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -3085,7 +3085,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 673276a..61e201e 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1391,7 +1391,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 fa8de13..61bf653 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2509,6 +2509,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 ||
@@ -2518,8 +2519,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 354e5d0..748ba3a 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1488,7 +1488,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 */
 
@@ -2989,7 +2989,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
@@ -3025,8 +3025,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 20b5273..7b6f49e 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -996,6 +996,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,
@@ -1016,18 +1018,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))
 						{
@@ -1093,8 +1101,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 f7f85b5..b5f3dce 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -150,7 +150,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 9001e20..19d7b44 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -459,7 +459,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..faafcaf 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,9 @@ 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 +196,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 f919cf8..9f3db93 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 896824a..c84d6d4 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -25,6 +25,9 @@ typedef struct BrinOptions
 } BrinOptions;
 
 #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 abe8877..4c32dd1 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 34e7339..89559ab 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 1ad4ed6..ad4895f 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(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 bfdfed8..aa73ee3 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -196,6 +196,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)
  */
@@ -291,7 +304,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);
@@ -391,4 +403,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 6289ffa..38f0257 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..1be9df0
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,224 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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_relopt) (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_relopt 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 */
+	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(char *namespace);
+extern void optionsCatalogAddItemBool(options_catalog * catalog, char *name,
+				char *desc, LOCKMODE lockmode, option_definition_flags flags,
+				int struct_offset, bool default_val);
+extern void optionsCatalogAddItemInt(options_catalog * catalog, char *name,
+			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, char *name,
+		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,
+	char *name, char *desc, LOCKMODE lockmode, option_definition_flags flags,
+	int struct_offset, const char **allowed_values, int default_val);
+extern void optionsCatalogAddItemString(options_catalog * catalog, char *name,
+	char *desc, LOCKMODE lockmode, option_definition_flags flags,
+	int struct_offset, char *default_val, validate_string_relopt 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 List *optionsDefListToRawValues(List *defList, options_parse_mode
+																parse_mode);
+extern Datum optionsValuesToTextArray(List *options_values);
+extern List *optionsTextArrayToRawValues(Datum array_datum);
+extern List *optionsMergeOptionValues(List *old_options, List *new_options);
+extern void optionsDefListValdateNamespaces(List *defList,
+											char **allowed_namespaces);
+extern List *optionsDefListFilterNamespaces(List *defList, char *namespace);
+extern List *optionsTextArrayToDefList(Datum options);
+extern List *optionsParseRawValues(List * raw_values, options_catalog *catalog,
+									options_parse_mode mode);
+extern bytea *optionsValuesToBytea(List * options, options_catalog *catalog);
+
+/*
+ * 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 861977a..78c45db 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,263 +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),
-	/* if you add a new kind, make sure you update "last_default" too */
-	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_BRIN,
-	/* 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) (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, char *name, char *desc,
-				   bool default_val);
-extern void add_int_reloption(bits32 kinds, char *name, char *desc,
-				  int default_val, int min_val, int max_val);
-extern void add_real_reloption(bits32 kinds, char *name, char *desc,
-				   double default_val, double min_val, double max_val);
-extern void add_string_reloption(bits32 kinds, char *name, char *desc,
-					 char *default_val, validate_string_relopt validator);
-
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
-					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 9dca8fd..a3b0a5e 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
@@ -183,7 +179,7 @@ typedef struct spgLeafConsistentOut
 
 
 /* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
+extern Datum spghandler(PG_FUNCTION_ARGS);
 
 /* spginsert.c */
 extern IndexBuildResult *spgbuild(Relation heap, Relation index,
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index 4072c05..38ac122 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 db7f145..d6464a7 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 f3a9701..33f5782 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -31,7 +31,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 a617a7c..28113ef 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -269,7 +269,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) */
@@ -277,107 +277,115 @@ typedef struct StdRdOptions
 	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 */
+
 /*
- * 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
@@ -556,7 +564,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 2227f2d..0a17ae9 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2285,7 +2285,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;
@@ -3372,3 +3374,10 @@ ERROR:  partition constraint is violated by some row
 -- cleanup
 drop table p;
 drop table p1;
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.out
index f0008dd3..c52b022 100644
--- a/src/test/regress/expected/brin.out
+++ b/src/test/regress/expected/brin.out
@@ -406,3 +406,8 @@ SELECT brin_summarize_new_values('brinidx'); -- ok, no change expected
                          0
 (1 row)
 
+-- 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/create_index.out b/src/test/regress/expected/create_index.out
index e519fdb..2ff134e 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2340,7 +2340,7 @@ CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
 WARNING:  hash indexes are not WAL-logged and their use is discouraged
 CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
 WARNING:  hash indexes are not WAL-logged and their use is discouraged
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
 WARNING:  hash indexes are not WAL-logged and their use is discouraged
 CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
 CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.out
index c7181b0..19f1881 100644
--- a/src/test/regress/expected/gist.out
+++ b/src/test/regress/expected/gist.out
@@ -12,6 +12,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)
diff --git a/src/test/regress/expected/hash_index.out b/src/test/regress/expected/hash_index.out
index f8b9f02..9f9352d 100644
--- a/src/test/regress/expected/hash_index.out
+++ b/src/test/regress/expected/hash_index.out
@@ -232,3 +232,14 @@ INSERT INTO hash_heap_float4 VALUES (1.1,1);
 CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
 WARNING:  hash indexes are not WAL-logged and their use is discouraged
 DROP TABLE hash_heap_float4 CASCADE;
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+WARNING:  hash indexes are not WAL-logged and their use is discouraged
+ERROR:  value 9 out of bounds for option "fillfactor"
+DETAIL:  Valid values are between "10" and "100".
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+WARNING:  hash indexes are not WAL-logged and their use is discouraged
+ERROR:  value 101 out of bounds for option "fillfactor"
+DETAIL:  Valid values are between "10" and "100".
+ALTER INDEX  hash_f8_index SET (fillfactor=99);
+ERROR:  changing parameter "fillfactor" is not allowed
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
new file mode 100644
index 0000000..835d72f
--- /dev/null
+++ b/src/test/regress/expected/reloptions.out
@@ -0,0 +1,193 @@
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+                                  reloptions                                  
+------------------------------------------------------------------------------
+ {fillfactor=30,autovacuum_enabled=false,autovacuum_analyze_scale_factor=0.2}
+(1 row)
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+   reloptions    
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+ERROR:  value 2 out of bounds for option "fillfactor"
+DETAIL:  Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+ERROR:  value 110 out of bounds for option "fillfactor"
+DETAIL:  Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+ERROR:  value -10.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL:  Valid values are between "0.000000" and "100.000000".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+ERROR:  value 110.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL:  Valid values are between "0.000000" and "100.000000".
+-- Fail when option and namespase do not exist
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+ERROR:  unrecognized parameter "not_existing_option"
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+ERROR:  unrecognized parameter namespace "not_existing_namespace"
+-- Fail while setting unproper value
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+ERROR:  invalid value for integer option "fillfactor": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+ERROR:  invalid value for integer option "fillfactor": string
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+ERROR:  invalid value for integer option "fillfactor": true
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+ERROR:  invalid value for boolean option "autovacuum_enabled": 12
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+ERROR:  invalid value for boolean option "autovacuum_enabled": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+ERROR:  invalid value for boolean option "autovacuum_enabled": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+ERROR:  invalid value for floating point option "autovacuum_analyze_scale_factor": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+ERROR:  invalid value for floating point option "autovacuum_analyze_scale_factor": true
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor); 
+ERROR:  invalid value for integer option "fillfactor": true
+-- Simple ALTER TABLE
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+                                  reloptions                                  
+------------------------------------------------------------------------------
+ {autovacuum_enabled=false,fillfactor=31,autovacuum_analyze_scale_factor=0.3}
+(1 row)
+
+-- Check that we cat set boolean option to true just by mentioning it
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+                                 reloptions                                  
+-----------------------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true,fillfactor=32}
+(1 row)
+
+-- Check that RESET works well
+ALTER TABLE reloptions_test RESET (fillfactor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+                          reloptions                           
+---------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true}
+(1 row)
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+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 
+------------
+ 
+(1 row)
+
+-- Check RESET fails on att=value
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+ERROR:  RESET must not include values for parameters
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+   reloptions    
+-----------------
+ {fillfactor=20}
+(1 row)
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+            reloptions             
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+            reloptions             
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=24}
+(1 row)
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions 
+------------
+ 
+(1 row)
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+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"
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+                   reloptions                    
+-------------------------------------------------
+ {autovacuum_vacuum_cost_delay=24,fillfactor=40}
+(1 row)
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+            reloptions             
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+   reloptions    
+-----------------
+ {fillfactor=30}
+(1 row)
+
+-- Fail when option and namespase do not exist
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+ERROR:  unrecognized parameter "not_existing_option"
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+ERROR:  unrecognized parameter namespace "not_existing_option"
+-- Check ranges
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+ERROR:  value 1 out of bounds for option "fillfactor"
+DETAIL:  Valid values are between "10" and "100".
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+ERROR:  value 130 out of bounds for option "fillfactor"
+DETAIL:  Valid values are between "10" and "100".
+-- Check alter
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+   reloptions    
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Check alter on empty relop list
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+   reloptions    
+-----------------
+ {fillfactor=40}
+(1 row)
+
diff --git a/src/test/regress/expected/spgist.out b/src/test/regress/expected/spgist.out
index 0691e91..bb58fb7 100644
--- a/src/test/regress/expected/spgist.out
+++ b/src/test/regress/expected/spgist.out
@@ -4,7 +4,7 @@
 -- There are other tests to test different SP-GiST opclasses. This is for
 -- testing SP-GiST code itself.
 create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
 -- Test vacuum-root operation. It gets invoked when the root is also a leaf,
 -- i.e. the index is very small.
 insert into spgist_point_tbl (id, p)
@@ -37,3 +37,12 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
 -- tuple to be moved to another page.
 insert into spgist_text_tbl (id, t)
 select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+ERROR:  value 9 out of bounds for option "fillfactor"
+DETAIL:  Valid values are between "10" and "100".
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+ERROR:  value 101 out of bounds for option "fillfactor"
+DETAIL:  Valid values are between "10" and "100".
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index aa06d1d..287dd84 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/input/tablespace.source b/src/test/regress/input/tablespace.source
index 03a62bd..7f7934b 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -12,10 +12,10 @@ DROP TABLESPACE regress_tblspacewith;
 CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
 
 -- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
 ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true);  -- fail
 ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
 
 -- create a schema we can use
 CREATE SCHEMA testschema;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index aaedf5f..2443511 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -14,12 +14,12 @@ DROP TABLESPACE regress_tblspacewith;
 -- create a tablespace we can use
 CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
 -- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
 ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true);  -- fail
 ERROR:  unrecognized parameter "some_nonexistent_parameter"
 ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
 ERROR:  RESET must not include values for parameters
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
 -- create a schema we can use
 CREATE SCHEMA testschema;
 -- try a table
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 9f38349..f35ece6 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -109,7 +109,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
 # NB: temp.sql does a reconnect which transiently uses 2 connections,
 # so keep this parallel group to at most 19 tests
 # ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml reloptions
 
 # event triggers cannot run concurrently with any test that runs DDL
 test: event_trigger
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 2987b24..8173710 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -173,5 +173,6 @@ test: returning
 test: largeobject
 test: with
 test: xml
+test: reloptions
 test: event_trigger
 test: stats
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 8cd6786..bd59482 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2228,3 +2228,11 @@ alter table p attach partition p1 for values from (1, 2) to (1, 10);
 -- cleanup
 drop table p;
 drop table p1;
+
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.sql
index 5bf5387..6353c82 100644
--- a/src/test/regress/sql/brin.sql
+++ b/src/test/regress/sql/brin.sql
@@ -409,3 +409,7 @@ UPDATE brintest SET textcol = '' WHERE textcol IS NOT NULL;
 SELECT brin_summarize_new_values('brintest'); -- error, not an index
 SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index
 SELECT brin_summarize_new_values('brinidx'); -- ok, no change expected
+
+-- 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/create_index.sql b/src/test/regress/sql/create_index.sql
index 1648072..1f8d101 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -682,7 +682,7 @@ CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
 
 CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
 
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
 
 CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
 CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.sql
index 9d4ff1e..ce1ac1a 100644
--- a/src/test/regress/sql/gist.sql
+++ b/src/test/regress/sql/gist.sql
@@ -17,6 +17,17 @@ 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/hash_index.sql b/src/test/regress/sql/hash_index.sql
index 15a3b06..b034efb 100644
--- a/src/test/regress/sql/hash_index.sql
+++ b/src/test/regress/sql/hash_index.sql
@@ -194,3 +194,8 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
 INSERT INTO hash_heap_float4 VALUES (1.1,1);
 CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
 DROP TABLE hash_heap_float4 CASCADE;
+
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+ALTER INDEX  hash_f8_index SET (fillfactor=99);
\ No newline at end of file
diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql
new file mode 100644
index 0000000..0bfec28
--- /dev/null
+++ b/src/test/regress/sql/reloptions.sql
@@ -0,0 +1,153 @@
+
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+
+-- Fail when option and namespase do not exist
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+
+-- Fail while setting unproper value
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor); 
+
+-- Simple ALTER TABLE
+
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that we cat set boolean option to true just by mentioning it
+
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESET works well
+
+ALTER TABLE reloptions_test RESET (fillfactor);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+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;
+
+-- Check RESET fails on att=value
+
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Fail on unexisting 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);
+
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Fail when option and namespase do not exist
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+
+-- Check ranges
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+
+-- Check alter
+
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Check alter on empty relop list
+
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+
+
diff --git a/src/test/regress/sql/spgist.sql b/src/test/regress/sql/spgist.sql
index 5896b50..8d26e49 100644
--- a/src/test/regress/sql/spgist.sql
+++ b/src/test/regress/sql/spgist.sql
@@ -5,7 +5,7 @@
 -- testing SP-GiST code itself.
 
 create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
 
 -- Test vacuum-root operation. It gets invoked when the root is also a leaf,
 -- i.e. the index is very small.
@@ -48,3 +48,10 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
 -- tuple to be moved to another page.
 insert into spgist_text_tbl (id, t)
 select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
