Обсуждение: Suggestion: Unified options API. Need help from core team

Поиск
Список
Период
Сортировка

Suggestion: Unified options API. Need help from core team

От
Nikolay Shaplov
Дата:
Hi!

I am still hoping to finish my work on reloptions I've started some years ago.

I've renewed my patch and I think I need help from core team to finish it.

General idea of the patch: Now we have three ways to define options for 
different objects, with more or less different code used for it. It wold be 
better to have unified context independent API for processing options, instead.

Long story short:

There is Option Specification object, that has all information about single 
option, how it should be parsed and validated.

There is Option Specification Set object, an array of Option Specs, that defines 
all options available for certain object (am of some index for example).

When some object (relation, opclass,  etc) wants to have an options, it 
creates an Option Spec Set for there options, and uses it for converting 
options between different representations (to get is from SQL, to store it in 
pg_class, to pass it to the core code as bytea etc)

For indexes Option Spec Set is available via Access Method API. 

For non-index relations all Option Spec Sets are left in reloption.c file, and 
should be moved to heap AM later. (They are not in AM now so will not change 
it now)

Main problem:

There are LockModes. LockModes for options is also stored in Option Spec Set. 
For indexes Option Spec Sec is accessable via AM. So to get LockMode for 
option of an index you need to have access for it's relation object (so you 
can call proper AM method to fetch spec set). So you need "Relation rel" in 
AlterTableGetRelOptionsLockLevel where Lock Level is determinated (src/
backend/access/common/reloptions.c)
AlterTableGetRelOptionsLockLevel is called from AlterTableGetLockLevel (src/
backend/commands/tablecmds.c) so we need "Relation rel" there too.
AlterTableGetLockLevel is called from AlterTableInternal (/src/backend/
commands/tablecmds.c) There we have "Oid relid" so we can try to open relation 
like this

                   Relation rel = relation_open(relid, NoLock);
                   cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
                                                   castNode(List, cmd->def));
                   relation_close(rel,NoLock);
                   break;

but this will trigger the assertion 

   Assert(lockmode != NoLock ||
          IsBootstrapProcessingMode() ||
          CheckRelationLockedByMe(r, c, true));

in relation_open (b/src/backend/access/common/relation.c)

For now I've commented this assertion out. I've tried to open relation with 
AccessShareLock but this caused one test to fail, and I am not sure this 
solution is better.

What I have done here I consider a hack, so I need a help of core-team here to 
do it in right way.

General problems:

I guess I need a coauthor, or supervisor from core team, to finish this patch. 
The amount of code is big, and I guess there are parts that can be made more 
in postgres way, then I did them. And I would need an advice there, and I 
guess it would be better to do if before sending it to commitfest.


Current patch status:

1. It is Beta. Some minor issues and FIXMEs are not solved. Some code comments 
needs revising, but in general it do what it is intended to do.

2. This patch does not intend to change postgres behavior at all, all should 
work as before, all changes are internal only.

The only exception is error message for unexciting option name in toast 
namespace 

 CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42);
-ERROR:  unrecognized parameter "not_existing_option"
+ERROR:  unrecognized parameter "toast.not_existing_option"

New message is better I guess, though I can change it back if needed.

3. I am doing my development in this blanch https://gitlab.com/dhyannataraj/
postgres/-/tree/new_options_take_two I am making changes every day, so last 
version will be available there

Would be glad to hear from coreteam before I finish with this patch and made it 
ready for commit-fest.



Вложения

Re: Suggestion: Unified options API. Need help from core team

От
Bruce Momjian
Дата:
Uh, the core team does not get involved in development issues, unless
there is a issue that clearly cannot be resolved by discussion on the
hackers list.

---------------------------------------------------------------------------

On Mon, Oct 18, 2021 at 04:24:23PM +0300, Nikolay Shaplov wrote:
> Hi!
> 
> I am still hoping to finish my work on reloptions I've started some years ago.
> 
> I've renewed my patch and I think I need help from core team to finish it.
> 
> General idea of the patch: Now we have three ways to define options for 
> different objects, with more or less different code used for it. It wold be 
> better to have unified context independent API for processing options, instead.
> 
> Long story short:
> 
> There is Option Specification object, that has all information about single 
> option, how it should be parsed and validated.
> 
> There is Option Specification Set object, an array of Option Specs, that defines 
> all options available for certain object (am of some index for example).
> 
> When some object (relation, opclass,  etc) wants to have an options, it 
> creates an Option Spec Set for there options, and uses it for converting 
> options between different representations (to get is from SQL, to store it in 
> pg_class, to pass it to the core code as bytea etc)
> 
> For indexes Option Spec Set is available via Access Method API. 
> 
> For non-index relations all Option Spec Sets are left in reloption.c file, and 
> should be moved to heap AM later. (They are not in AM now so will not change 
> it now)
> 
> Main problem:
> 
> There are LockModes. LockModes for options is also stored in Option Spec Set. 
> For indexes Option Spec Sec is accessable via AM. So to get LockMode for 
> option of an index you need to have access for it's relation object (so you 
> can call proper AM method to fetch spec set). So you need "Relation rel" in 
> AlterTableGetRelOptionsLockLevel where Lock Level is determinated (src/
> backend/access/common/reloptions.c)
> AlterTableGetRelOptionsLockLevel is called from AlterTableGetLockLevel (src/
> backend/commands/tablecmds.c) so we need "Relation rel" there too.
> AlterTableGetLockLevel is called from AlterTableInternal (/src/backend/
> commands/tablecmds.c) There we have "Oid relid" so we can try to open relation 
> like this
> 
>                    Relation rel = relation_open(relid, NoLock);
>                    cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
>                                                    castNode(List, cmd->def));
>                    relation_close(rel,NoLock);
>                    break;
> 
> but this will trigger the assertion 
> 
>    Assert(lockmode != NoLock ||
>           IsBootstrapProcessingMode() ||
>           CheckRelationLockedByMe(r, c, true));
> 
> in relation_open (b/src/backend/access/common/relation.c)
> 
> For now I've commented this assertion out. I've tried to open relation with 
> AccessShareLock but this caused one test to fail, and I am not sure this 
> solution is better.
> 
> What I have done here I consider a hack, so I need a help of core-team here to 
> do it in right way.
> 
> General problems:
> 
> I guess I need a coauthor, or supervisor from core team, to finish this patch. 
> The amount of code is big, and I guess there are parts that can be made more 
> in postgres way, then I did them. And I would need an advice there, and I 
> guess it would be better to do if before sending it to commitfest.
> 
> 
> Current patch status:
> 
> 1. It is Beta. Some minor issues and FIXMEs are not solved. Some code comments 
> needs revising, but in general it do what it is intended to do.
> 
> 2. This patch does not intend to change postgres behavior at all, all should 
> work as before, all changes are internal only.
> 
> The only exception is error message for unexciting option name in toast 
> namespace 
> 
>  CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42);
> -ERROR:  unrecognized parameter "not_existing_option"
> +ERROR:  unrecognized parameter "toast.not_existing_option"
> 
> New message is better I guess, though I can change it back if needed.
> 
> 3. I am doing my development in this blanch https://gitlab.com/dhyannataraj/
> postgres/-/tree/new_options_take_two I am making changes every day, so last 
> version will be available there
> 
> Would be glad to hear from coreteam before I finish with this patch and made it 
> ready for commit-fest.
> 
> 

> diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
> index a22a6df..8f2d5e7 100644
> --- a/contrib/bloom/bloom.h
> +++ b/contrib/bloom/bloom.h
> @@ -17,6 +17,7 @@
>  #include "access/generic_xlog.h"
>  #include "access/itup.h"
>  #include "access/xlog.h"
> +#include "access/options.h"
>  #include "fmgr.h"
>  #include "nodes/pathnodes.h"
>  
> @@ -207,7 +208,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
>                                             void *callback_state);
>  extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
>                                                IndexBulkDeleteResult *stats);
> -extern bytea *bloptions(Datum reloptions, bool validate);
> +extern void *blrelopt_specset(void);
> +extern void blReloptionPostprocess(void *, bool validate);
>  extern void blcostestimate(PlannerInfo *root, IndexPath *path,
>                             double loop_count, Cost *indexStartupCost,
>                             Cost *indexTotalCost, Selectivity *indexSelectivity,
> diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
> index 754de00..54dad16 100644
> --- a/contrib/bloom/blutils.c
> +++ b/contrib/bloom/blutils.c
> @@ -15,7 +15,7 @@
>  
>  #include "access/amapi.h"
>  #include "access/generic_xlog.h"
> -#include "access/reloptions.h"
> +#include "access/options.h"
>  #include "bloom.h"
>  #include "catalog/index.h"
>  #include "commands/vacuum.h"
> @@ -34,53 +34,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_spec_set *bl_relopt_specset;
>  
>  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,
> -                      AccessExclusiveLock);
> -    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,
> -                          AccessExclusiveLock);
> -        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;
> -    }
> -}
> -
> -/*
>   * Construct a default set of Bloom options.
>   */
>  static BloomOptions *
> @@ -135,7 +95,7 @@ blhandler(PG_FUNCTION_ARGS)
>      amroutine->amvacuumcleanup = blvacuumcleanup;
>      amroutine->amcanreturn = NULL;
>      amroutine->amcostestimate = blcostestimate;
> -    amroutine->amoptions = bloptions;
> +    amroutine->amreloptspecset = blrelopt_specset;
>      amroutine->amproperty = NULL;
>      amroutine->ambuildphasename = NULL;
>      amroutine->amvalidate = blvalidate;
> @@ -154,6 +114,28 @@ blhandler(PG_FUNCTION_ARGS)
>      PG_RETURN_POINTER(amroutine);
>  }
>  
> +void
> +blReloptionPostprocess(void *data, bool validate)
> +{
> +    BloomOptions *opts = (BloomOptions *) data;
> +    int            i;
> +
> +    if (validate)
> +        for (i = 0; i < INDEX_MAX_KEYS; i++)
> +        {
> +            if (opts->bitSize[i] >= opts->bloomLength)
> +            {
> +                ereport(ERROR,
> +                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> +                       errmsg("col%i should not be grater than length", i)));
> +            }
> +        }
> +
> +    /* Convert signature length from # of bits to # to words, rounding up */
> +    opts->bloomLength = (opts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
> +}
> +
> +
>  /*
>   * Fill BloomState structure for particular index.
>   */
> @@ -474,24 +456,39 @@ BloomInitMetapage(Relation index)
>      UnlockReleaseBuffer(metaBuffer);
>  }
>  
> -/*
> - * Parse reloptions for bloom index, producing a BloomOptions struct.
> - */
> -bytea *
> -bloptions(Datum reloptions, bool validate)
> +void *
> +blrelopt_specset(void)
>  {
> -    BloomOptions *rdopts;
> +    int            i;
> +    char        buf[16];
>  
> -    /* Parse the user-given reloptions */
> -    rdopts = (BloomOptions *) build_reloptions(reloptions, validate,
> -                                               bl_relopt_kind,
> -                                               sizeof(BloomOptions),
> -                                               bl_relopt_tab,
> -                                               lengthof(bl_relopt_tab));
> +    if (bl_relopt_specset)
> +        return bl_relopt_specset;
>  
> -    /* Convert signature length from # of bits to # to words, rounding up */
> -    if (rdopts)
> -        rdopts->bloomLength = (rdopts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
>  
> -    return (bytea *) rdopts;
> +    bl_relopt_specset = allocateOptionsSpecSet(NULL,
> +                               sizeof(BloomOptions), INDEX_MAX_KEYS + 1);
> +    bl_relopt_specset->postprocess_fun = blReloptionPostprocess;
> +
> +    optionsSpecSetAddInt(bl_relopt_specset, "length",
> +                             "Length of signature in bits",
> +                             NoLock,        /* No lock as far as ALTER is
> +                                             * forbidden */
> +                             0,
> +                             offsetof(BloomOptions, bloomLength),
> +                             DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH);
> +
> +    /* 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);
> +        optionsSpecSetAddInt(bl_relopt_specset, buf,
> +                               "Number of bits for corresponding column",
> +                                 NoLock,    /* No lock as far as ALTER is
> +                                             * forbidden */
> +                                 0,
> +                                 offsetof(BloomOptions, bitSize[i]),
> +                                 DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS);
> +    }
> +    return bl_relopt_specset;
>  }
> diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
> index dae12a7..e79456d 100644
> --- a/contrib/bloom/expected/bloom.out
> +++ b/contrib/bloom/expected/bloom.out
> @@ -228,3 +228,6 @@ CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
>  ERROR:  value 0 out of bounds for option "length"
>  CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
>  ERROR:  value 0 out of bounds for option "col1"
> +-- 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
> diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
> index 4733e1e..0bfc767 100644
> --- a/contrib/bloom/sql/bloom.sql
> +++ b/contrib/bloom/sql/bloom.sql
> @@ -93,3 +93,6 @@ SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
>  \set VERBOSITY terse
>  CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
>  CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
> +
> +-- check post_validate for colN<lengh
> +CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
> diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
> index 3a0beaa..a15a10b 100644
> --- a/contrib/dblink/dblink.c
> +++ b/contrib/dblink/dblink.c
> @@ -2005,7 +2005,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 2c2f149..1194747 100644
> --- a/contrib/file_fdw/file_fdw.c
> +++ b/contrib/file_fdw/file_fdw.c
> @@ -195,7 +195,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 5bb1af4..bbd4167 100644
> --- a/contrib/postgres_fdw/option.c
> +++ b/contrib/postgres_fdw/option.c
> @@ -72,7 +72,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 ccc9fa0..5dd52a4 100644
> --- a/src/backend/access/brin/brin.c
> +++ b/src/backend/access/brin/brin.c
> @@ -20,7 +20,6 @@
>  #include "access/brin_pageops.h"
>  #include "access/brin_xlog.h"
>  #include "access/relation.h"
> -#include "access/reloptions.h"
>  #include "access/relscan.h"
>  #include "access/table.h"
>  #include "access/tableam.h"
> @@ -40,7 +39,6 @@
>  #include "utils/memutils.h"
>  #include "utils/rel.h"
>  
> -
>  /*
>   * We use a BrinBuildState during initial construction of a BRIN index.
>   * The running state is kept in a BrinMemTuple.
> @@ -119,7 +117,6 @@ brinhandler(PG_FUNCTION_ARGS)
>      amroutine->amvacuumcleanup = brinvacuumcleanup;
>      amroutine->amcanreturn = NULL;
>      amroutine->amcostestimate = brincostestimate;
> -    amroutine->amoptions = brinoptions;
>      amroutine->amproperty = NULL;
>      amroutine->ambuildphasename = NULL;
>      amroutine->amvalidate = brinvalidate;
> @@ -134,6 +131,7 @@ brinhandler(PG_FUNCTION_ARGS)
>      amroutine->amestimateparallelscan = NULL;
>      amroutine->aminitparallelscan = NULL;
>      amroutine->amparallelrescan = NULL;
> +    amroutine->amreloptspecset = bringetreloptspecset;
>  
>      PG_RETURN_POINTER(amroutine);
>  }
> @@ -963,23 +961,6 @@ brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
>  }
>  
>  /*
> - * reloptions processor for BRIN indexes
> - */
> -bytea *
> -brinoptions(Datum reloptions, bool validate)
> -{
> -    static const relopt_parse_elt tab[] = {
> -        {"pages_per_range", RELOPT_TYPE_INT, offsetof(BrinOptions, pagesPerRange)},
> -        {"autosummarize", RELOPT_TYPE_BOOL, offsetof(BrinOptions, autosummarize)}
> -    };
> -
> -    return (bytea *) build_reloptions(reloptions, validate,
> -                                      RELOPT_KIND_BRIN,
> -                                      sizeof(BrinOptions),
> -                                      tab, lengthof(tab));
> -}
> -
> -/*
>   * SQL-callable function to scan through an index and summarize all ranges
>   * that are not currently summarized.
>   */
> @@ -1765,3 +1746,32 @@ check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys)
>  
>      return true;
>  }
> +
> +static options_spec_set *brin_relopt_specset = NULL;
> +
> +void *
> +bringetreloptspecset(void)
> +{
> +    if (brin_relopt_specset)
> +        return brin_relopt_specset;
> +    brin_relopt_specset = allocateOptionsSpecSet(NULL,
> +                                                 sizeof(BrinOptions), 2);
> +
> +    optionsSpecSetAddInt(brin_relopt_specset, "pages_per_range",
> +           "Number of pages that each page range covers in a BRIN index",
> +                             NoLock,        /* since ALTER is not allowed
> +                                             * no lock needed */
> +                             0,
> +                             offsetof(BrinOptions, pagesPerRange),
> +                             BRIN_DEFAULT_PAGES_PER_RANGE,
> +                             BRIN_MIN_PAGES_PER_RANGE,
> +                             BRIN_MAX_PAGES_PER_RANGE);
> +        optionsSpecSetAddBool(brin_relopt_specset, "autosummarize",
> +                    "Enables automatic summarization on this BRIN index",
> +                              AccessExclusiveLock,
> +                              0,
> +                              offsetof(BrinOptions, autosummarize),
> +                              false);
> +    return brin_relopt_specset;
> +}
> +
> diff --git a/src/backend/access/brin/brin_pageops.c b/src/backend/access/brin/brin_pageops.c
> index df9ffc2..1940b3d 100644
> --- a/src/backend/access/brin/brin_pageops.c
> +++ b/src/backend/access/brin/brin_pageops.c
> @@ -420,6 +420,9 @@ brin_doinsert(Relation idxrel, BlockNumber pagesPerRange,
>          freespace = br_page_get_freespace(page);
>  
>      ItemPointerSet(&tid, blk, off);
> +
> +//elog(WARNING, "pages_per_range = %i", pagesPerRange);
> +
>      brinSetHeapBlockItemptr(revmapbuf, pagesPerRange, heapBlk, tid);
>      MarkBufferDirty(revmapbuf);
>  
> diff --git a/src/backend/access/common/Makefile b/src/backend/access/common/Makefile
> index b9aff0c..78c9c5a 100644
> --- a/src/backend/access/common/Makefile
> +++ b/src/backend/access/common/Makefile
> @@ -18,6 +18,7 @@ OBJS = \
>      detoast.o \
>      heaptuple.o \
>      indextuple.o \
> +    options.o \
>      printsimple.o \
>      printtup.o \
>      relation.o \
> diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
> new file mode 100644
> index 0000000..752cddc
> --- /dev/null
> +++ b/src/backend/access/common/options.c
> @@ -0,0 +1,1468 @@
> +/*-------------------------------------------------------------------------
> + *
> + * options.c
> + *      An unifom, context-free API for processing name=value options. Used
> + *      to process relation optons (reloptions), attribute options, opclass
> + *      options, etc.
> + *
> + * Portions Copyright (c) 1996-2021, 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"
> +
> +
> +/*
> + * OPTIONS SPECIFICATION and OPTION SPECIFICATION SET
> + *
> + * Each option is defined via Option Specification object (Option Spec).
> + * Option Spec should have all information that is needed for processing
> + * (parsing, validating, converting) of a single option. Implemented via set of
> + * option_spec_* structures.
> + *
> + * A set of Option Specs (Options Spec Set), defines all options available for
> + * certain object (certain relation kind for example). It is a list of
> + * Options Specs, plus validation functions that can be used to validate whole
> + * option set, if needed. Implemenred via options_spec_set structure and set of
> + * optionsSpecSetAdd* functions that are used for adding Option Specs items to
> + * a Set.
> + *
> + * NOTE: we choose therm "sepcification" instead of "definition" because therm
> + * "definition" is used for objects that came from lexer. So to avoud confusion
> + * here we have Option Specifications, and all "definitions" are from lexer.
> + */
> +
> +/*
> + * OPTION VALUES REPRESENTATIONS
> + *
> + * Option values usually came from lexer in form of defList obect, stored in
> + * pg_catalog as text array, and used when they are stored in memory as
> + * C-structure. These are different option values representations. Here goes
> + * brief description of all representations used in the code.
> + *
> + * Values
> + *
> + * Values are an internal representation that is used while converting
> + * Values between other representation. Value is called "parsed",
> + * when Value's value is converted to a proper type and validated, or is called
> + * "unparsed", when Value's value is stored as raw string that was obtained
> + * from the source without any cheks. In convertation funcion names first case
> + * is refered as Values, second case is refered as RawValues. Values is
> + * implemented as List of option_value C-structures.
> + *
> + * defList
> + *
> + * Options in form of definition List that comes from lexer. (For reloptions it
> + * is a part of SQL query that goes after WITH, SET or RESET keywords). Can be
> + * converted to and from Values using optionsDefListToRawValues and
> + * optionsTextArrayToRawValues functions.
> + *
> + * TEXT[]
> + *
> + * Options in form suitable for storig in TEXT[] field in DB. (E.g. reloptions
> + * are stores in pg_catalog.pg_class table in reloptions field). Can be converted
> + * to and from Values using optionsValuesToTextArray and optionsTextArrayToRawValues
> + * functions.
> + *
> + * Bytea
> + *
> + * Option data stored in C-structure with varlena header in the beginning of the
> + * structure. This representation is used to pass option values to the core
> + * postgres. It is fast to read, it can be cached and so on. Bytea rpresentation
> + * can be obtained from Vales using optionsValuesToBytea function, and can't be
> + * converted back.
> + */
> +
> +static option_spec_basic *allocateOptionSpec(int type, const char *name,
> +                         const char *desc, LOCKMODE lockmode,
> +                         option_spec_flags flags, int struct_offset);
> +
> +static void parse_one_option(option_value * option, const char *text_str,
> +                 int text_len, bool validate);
> +static void *optionsAllocateBytea(options_spec_set * spec_set, List *options);
> +
> +
> +static List *
> +optionsDefListToRawValues(List *defList, options_parse_mode
> +                          parse_mode);
> +static Datum optionsValuesToTextArray(List *options_values);
> +static List *optionsMergeOptionValues(List *old_options, List *new_options);
> +static bytea *optionsValuesToBytea(List *options, options_spec_set * spec_set);
> +List *optionsTextArrayToRawValues(Datum array_datum);
> +List *optionsParseRawValues(List *raw_values, options_spec_set * spec_set,
> +                      options_parse_mode mode);
> +
> +
> +/*
> + * Options spec_set 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_spec_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.
> +*/
> +
> +/*
> + * allocateOptionsSpecSet
> + *        Creates new Option Spec Set object: Allocates memory and initializes
> + *        structure members.
> + *
> + * Spec Set items can be add via allocateOptionSpec and optionSpecSetAddItem functions
> + * or by calling directly any of optionsSpecSetAdd* function (preferable way)
> + *
> + * namespace - Spec Set can be bind to certain namespace (E.g.
> + * namespace.option=value). Options from other namespaces will be ignored while
> + * processing. If set to NULL, no namespace will be used at all.
> + *
> + * size_of_bytea - size of target structure of Bytea options represenation
> + *
> + * num_items_expected - if you know expected number of Spec Set items set it here.
> + * Set to -1 in other cases. num_items_expected will be used for preallocating memory
> + * and will trigger error, if you try to add more items than you expected.
> + */
> +
> +options_spec_set *
> +allocateOptionsSpecSet(const char *namespace, int size_of_bytea, int num_items_expected)
> +{
> +    MemoryContext oldcxt;
> +    options_spec_set *spec_set;
> +
> +    oldcxt = MemoryContextSwitchTo(TopMemoryContext);
> +    spec_set = palloc(sizeof(options_spec_set));
> +    if (namespace)
> +    {
> +        spec_set->namespace = palloc(strlen(namespace) + 1);
> +        strcpy(spec_set->namespace, namespace);
> +    }
> +    else
> +        spec_set->namespace = NULL;
> +    if (num_items_expected > 0)
> +    {
> +        spec_set->num_allocated = num_items_expected;
> +        spec_set->forbid_realloc = true;
> +        spec_set->definitions = palloc(
> +                 spec_set->num_allocated * sizeof(option_spec_basic *));
> +    }
> +    else
> +    {
> +        spec_set->num_allocated = 0;
> +        spec_set->forbid_realloc = false;
> +        spec_set->definitions = NULL;
> +    }
> +    spec_set->num = 0;
> +    spec_set->struct_size = size_of_bytea;
> +    spec_set->postprocess_fun = NULL;
> +    MemoryContextSwitchTo(oldcxt);
> +    return spec_set;
> +}
> +
> +/*
> + * allocateOptionSpec
> + *        Allocates a new Option Specifiation object of desired type and
> + *        initialize the type-independent fields
> + */
> +static option_spec_basic *
> +allocateOptionSpec(int type, const char *name, const char *desc, LOCKMODE lockmode,
> +                         option_spec_flags flags, int struct_offset)
> +{
> +    MemoryContext oldcxt;
> +    size_t        size;
> +    option_spec_basic *newoption;
> +
> +    oldcxt = MemoryContextSwitchTo(TopMemoryContext);
> +
> +    switch (type)
> +    {
> +        case OPTION_TYPE_BOOL:
> +            size = sizeof(option_spec_bool);
> +            break;
> +        case OPTION_TYPE_INT:
> +            size = sizeof(option_spec_int);
> +            break;
> +        case OPTION_TYPE_REAL:
> +            size = sizeof(option_spec_real);
> +            break;
> +        case OPTION_TYPE_ENUM:
> +            size = sizeof(option_spec_enum);
> +            break;
> +        case OPTION_TYPE_STRING:
> +            size = sizeof(option_spec_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;
> +}
> +
> +/*
> + * optionSpecSetAddItem
> + *        Adds pre-created Option Specification objec to the Spec Set
> + */
> +static void
> +optionSpecSetAddItem(option_spec_basic * newoption,
> +                     options_spec_set * spec_set)
> +{
> +    if (spec_set->num >= spec_set->num_allocated)
> +    {
> +        MemoryContext oldcxt;
> +
> +        Assert(!spec_set->forbid_realloc);
> +        oldcxt = MemoryContextSwitchTo(TopMemoryContext);
> +
> +        if (spec_set->num_allocated == 0)
> +        {
> +            spec_set->num_allocated = 8;
> +            spec_set->definitions = palloc(
> +                 spec_set->num_allocated * sizeof(option_spec_basic *));
> +        }
> +        else
> +        {
> +            spec_set->num_allocated *= 2;
> +            spec_set->definitions = repalloc(spec_set->definitions,
> +                 spec_set->num_allocated * sizeof(option_spec_basic *));
> +        }
> +        MemoryContextSwitchTo(oldcxt);
> +    }
> +    spec_set->definitions[spec_set->num] = newoption;
> +    spec_set->num++;
> +}
> +
> +
> +/*
> + * optionsSpecSetAddBool
> + *        Adds boolean Option Specification entry to the Spec Set
> + */
> +void
> +optionsSpecSetAddBool(options_spec_set * spec_set, const char *name, const char *desc,
> +                          LOCKMODE lockmode, option_spec_flags flags,
> +                          int struct_offset, bool default_val)
> +{
> +    option_spec_bool *spec_set_item;
> +
> +    spec_set_item = (option_spec_bool *)
> +        allocateOptionSpec(OPTION_TYPE_BOOL, name, desc, lockmode,
> +                                 flags, struct_offset);
> +
> +    spec_set_item->default_val = default_val;
> +
> +    optionSpecSetAddItem((option_spec_basic *) spec_set_item, spec_set);
> +}
> +
> +/*
> + * optionsSpecSetAddInt
> + *        Adds integer Option Specification entry to the Spec Set
> + */
> +void
> +optionsSpecSetAddInt(options_spec_set * spec_set, const char *name,
> +          const char *desc, LOCKMODE lockmode, option_spec_flags flags,
> +                int struct_offset, int default_val, int min_val, int max_val)
> +{
> +    option_spec_int *spec_set_item;
> +
> +    spec_set_item = (option_spec_int *)
> +        allocateOptionSpec(OPTION_TYPE_INT, name, desc, lockmode,
> +                                 flags, struct_offset);
> +
> +    spec_set_item->default_val = default_val;
> +    spec_set_item->min = min_val;
> +    spec_set_item->max = max_val;
> +
> +    optionSpecSetAddItem((option_spec_basic *) spec_set_item, spec_set);
> +}
> +
> +/*
> + * optionsSpecSetAddReal
> + *        Adds float Option Specification entry to the Spec Set
> + */
> +void
> +optionsSpecSetAddReal(options_spec_set * spec_set, const char *name, const char *desc,
> +         LOCKMODE lockmode, option_spec_flags flags, int struct_offset,
> +                          double default_val, double min_val, double max_val)
> +{
> +    option_spec_real *spec_set_item;
> +
> +    spec_set_item = (option_spec_real *)
> +        allocateOptionSpec(OPTION_TYPE_REAL, name, desc, lockmode,
> +                                 flags, struct_offset);
> +
> +    spec_set_item->default_val = default_val;
> +    spec_set_item->min = min_val;
> +    spec_set_item->max = max_val;
> +
> +    optionSpecSetAddItem((option_spec_basic *) spec_set_item, spec_set);
> +}
> +
> +/*
> + * optionsSpecSetAddEnum
> + *        Adds enum Option Specification entry to the Spec Set
> + *
> + * The members array must have a terminating NULL entry.
> + *
> + * The detailmsg is shown when unsupported values are passed, and has this
> + * form:   "Valid values are \"foo\", \"bar\", and \"bar\"."
> + *
> + * The members array and detailmsg are not copied -- caller must ensure that
> + * they are valid throughout the life of the process.
> + */
> +
> +void
> +optionsSpecSetAddEnum(options_spec_set * spec_set, const char *name, const char *desc,
> +        LOCKMODE lockmode, option_spec_flags flags, int struct_offset,
> +        opt_enum_elt_def * members, int default_val, const char *detailmsg)
> +{
> +    option_spec_enum *spec_set_item;
> +
> +    spec_set_item = (option_spec_enum *)
> +        allocateOptionSpec(OPTION_TYPE_ENUM, name, desc, lockmode,
> +                                 flags, struct_offset);
> +
> +    spec_set_item->default_val = default_val;
> +    spec_set_item->members = members;
> +    spec_set_item->detailmsg = detailmsg;
> +
> +    optionSpecSetAddItem((option_spec_basic *) spec_set_item, spec_set);
> +}
> +
> +/*
> + * optionsSpecSetAddString
> + *        Adds string Option Specification entry to the Spec Set
> + *
> + * "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
> +optionsSpecSetAddString(options_spec_set * spec_set, const char *name, const char *desc,
> +         LOCKMODE lockmode, option_spec_flags flags, int struct_offset,
> +                   const char *default_val, validate_string_option validator)
> +{
> +    option_spec_string *spec_set_item;
> +
> +    /* make sure the validator/default combination is sane */
> +    if (validator)
> +        (validator) (default_val);
> +
> +    spec_set_item = (option_spec_string *)
> +        allocateOptionSpec(OPTION_TYPE_STRING, name, desc, lockmode,
> +                                 flags, struct_offset);
> +    spec_set_item->validate_cb = validator;
> +
> +    if (default_val)
> +        spec_set_item->default_val = MemoryContextStrdup(TopMemoryContext,
> +                                                        default_val);
> +    else
> +        spec_set_item->default_val = NULL;
> +    optionSpecSetAddItem((option_spec_basic *) spec_set_item, spec_set);
> +}
> +
> +
> +/*
> + * Options transform functions
> + */
> +
> +/* FIXME this comment should be updated
> + * 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 spec_set
> + * 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 spec_set 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 spec_set.
> + *
> + * 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 spec_set 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 spec_set data, and then use
> + * it as a usual Datum object, when needed. This Datum should be cached
> + * somewhere (for example in rel->rd_options for relations) when object that
> + * has option is loaded from db.
> + */
> +
> +
> +/* optionsDefListToRawValues
> + *        Converts option values that came from syntax analyzer (DefList) into
> + *        Values List.
> + *
> + * No parsing is done here except for checking that RESET syntax is correct
> + * (syntax analyzer do not see difference between SET and RESET cases, we
> + * should treat it here manually
> + */
> +static List *
> +optionsDefListToRawValues(List *defList, options_parse_mode parse_mode)
> +{
> +    ListCell   *cell;
> +    List       *result = NIL;
> +
> +    foreach(cell, defList)
> +    {
> +        option_value *option_dst;
> +        DefElem    *def = (DefElem *) lfirst(cell);
> +        char       *value;
> +
> +        option_dst = palloc(sizeof(option_value));
> +
> +        if (def->defnamespace)
> +        {
> +            option_dst->namespace = palloc(strlen(def->defnamespace) + 1);
> +            strcpy(option_dst->namespace, def->defnamespace);
> +        }
> +        else
> +        {
> +            option_dst->namespace = NULL;
> +        }
> +        option_dst->raw_name = palloc(strlen(def->defname) + 1);
> +        strcpy(option_dst->raw_name, def->defname);
> +
> +        if (parse_mode & OPTIONS_PARSE_MODE_FOR_RESET)
> +        {
> +            /*
> +             * If this option came from RESET statement we should throw error
> +             * it it brings us name=value data, as syntax analyzer do not
> +             * prevent it
> +             */
> +            if (def->arg != NULL)
> +                ereport(ERROR,
> +                        (errcode(ERRCODE_SYNTAX_ERROR),
> +                    errmsg("RESET must not include values for parameters")));
> +
> +            option_dst->status = OPTION_VALUE_STATUS_FOR_RESET;
> +        }
> +        else
> +        {
> +            /*
> +             * For SET statement we should treat (name) expression as if it is
> +             * actually (name=true) so do it here manually. In other cases
> +             * just use value as we should use it
> +             */
> +            option_dst->status = OPTION_VALUE_STATUS_RAW;
> +            if (def->arg != NULL)
> +                value = defGetString(def);
> +            else
> +                value = "true";
> +            option_dst->raw_value = palloc(strlen(value) + 1);
> +            strcpy(option_dst->raw_value, value);
> +        }
> +
> +        result = lappend(result, option_dst);
> +    }
> +    return result;
> +}
> +
> +/*
> + * optionsValuesToTextArray
> + *        Converts List of option_values into TextArray
> + *
> + *    Convertation is made to put options into database (e.g. in
> + *    pg_class.reloptions for all relation options)
> + */
> +
> +Datum
> +optionsValuesToTextArray(List *options_values)
> +{
> +    ArrayBuildState *astate = NULL;
> +    ListCell   *cell;
> +    Datum        result;
> +
> +    foreach(cell, options_values)
> +    {
> +        option_value *option = (option_value *) lfirst(cell);
> +        const char *name;
> +        char       *value;
> +        text       *t;
> +        int            len;
> +
> +        /*
> +         * Raw value were not cleared while parsing, so instead of converting
> +         * it back, just use it to store value as text
> +         */
> +        value = option->raw_value;
> +
> +        Assert(option->status != OPTION_VALUE_STATUS_EMPTY);
> +
> +        /*
> +         * Name will be taken from option definition, if option were parsed or
> +         * from raw_name if option were not parsed for some reason
> +         */
> +        if (option->status == OPTION_VALUE_STATUS_PARSED)
> +            name = option->gen->name;
> +        else
> +            name = option->raw_name;
> +
> +        /*
> +         * Now build "name=value" string and append it to the array
> +         */
> +        len = VARHDRSZ + strlen(name) + strlen(value) + 1;
> +        t = (text *) palloc(len + 1);
> +        SET_VARSIZE(t, len);
> +        sprintf(VARDATA(t), "%s=%s", name, value);
> +        astate = accumArrayResult(astate, PointerGetDatum(t), false,
> +                                  TEXTOID, CurrentMemoryContext);
> +    }
> +    if (astate)
> +        result = makeArrayResult(astate, CurrentMemoryContext);
> +    else
> +        result = (Datum) 0;
> +
> +    return result;
> +}
> +
> +/*
> + * optionsTextArrayToRawValues
> + *        Converts options from TextArray format into RawValues list.
> + *
> + *    This function is used to convert options data that comes from database to
> + *    List of option_values, for further parsing, and, in the case of ALTER
> + *    command, for merging with new option values.
> + */
> +List *
> +optionsTextArrayToRawValues(Datum array_datum)
> +{
> +    List       *result = NIL;
> +
> +    if (PointerIsValid(DatumGetPointer(array_datum)))
> +    {
> +        ArrayType  *array = DatumGetArrayTypeP(array_datum);
> +        Datum       *options;
> +        int            noptions;
> +        int            i;
> +
> +        deconstruct_array(array, TEXTOID, -1, false, 'i',
> +                          &options, NULL, &noptions);
> +
> +        for (i = 0; i < noptions; i++)
> +        {
> +            option_value *option_dst;
> +            char       *text_str = VARDATA(options[i]);
> +            int            text_len = VARSIZE(options[i]) - VARHDRSZ;
> +            int            i;
> +            int            name_len = -1;
> +            char       *name;
> +            int            raw_value_len;
> +            char       *raw_value;
> +
> +            /*
> +             * Find position of '=' sign and treat id as a separator between
> +             * name and value in "name=value" item
> +             */
> +            for (i = 0; i < text_len; i = i + pg_mblen(text_str))
> +            {
> +                if (text_str[i] == '=')
> +                {
> +                    name_len = i;
> +                    break;
> +                }
> +            }
> +            Assert(name_len >= 1);        /* Just in case */
> +
> +            raw_value_len = text_len - name_len - 1;
> +
> +            /*
> +             * Copy name from src
> +             */
> +            name = palloc(name_len + 1);
> +            memcpy(name, text_str, name_len);
> +            name[name_len] = '\0';
> +
> +            /*
> +             * Copy value from src
> +             */
> +            raw_value = palloc(raw_value_len + 1);
> +            memcpy(raw_value, text_str + name_len + 1, raw_value_len);
> +            raw_value[raw_value_len] = '\0';
> +
> +            /*
> +             * Create new option_value item
> +             */
> +            option_dst = palloc(sizeof(option_value));
> +            option_dst->status = OPTION_VALUE_STATUS_RAW;
> +            option_dst->raw_name = name;
> +            option_dst->raw_value = raw_value;
> +            option_dst->namespace = NULL;
> +
> +            result = lappend(result, option_dst);
> +        }
> +    }
> +    return result;
> +}
> +
> +/*
> + * optionsMergeOptionValues
> + *        Merges two lists of option_values into one list
> + *
> + * This function is used to merge two Values list into one. It is used for all
> + * kinds of ALTER commands when existing options are merged|replaced with new
> + * options list. This function also process RESET variant of ALTER command. It
> + * merges two lists as usual, and then removes all items with RESET flag on.
> + *
> + * Both incoming lists will be destroyed while merging
> + */
> +static List *
> +optionsMergeOptionValues(List *old_options, List *new_options)
> +{
> +    List       *result = NIL;
> +    ListCell   *old_cell;
> +    ListCell   *new_cell;
> +
> +    /*
> +     * First add to result all old options that are not mentioned in new list
> +     */
> +    foreach(old_cell, old_options)
> +    {
> +        bool        found;
> +        const char *old_name;
> +        option_value *old_option;
> +
> +        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;
> +
> +        /*
> +         * Looking for a new option with same name
> +         */
> +        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 (strcmp(new_name, old_name) == 0)
> +            {
> +                found = true;
> +                break;
> +            }
> +        }
> +        if (!found)
> +            result = lappend(result, old_option);
> +    }
> +    /*
> +     * Now add all to result all new options that are not designated for reset
> +     */
> +    foreach(new_cell, new_options)
> +    {
> +        option_value *new_option;
> +        new_option = (option_value *) lfirst(new_cell);
> +
> +        if(new_option->status != OPTION_VALUE_STATUS_FOR_RESET)
> +            result = lappend(result, new_option);
> +    }
> +    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 (strcmp(def->defnamespace,
> +                                  allowed_namespaces[i]) == 0)
> +                {
> +                    found = true;
> +                    break;
> +                }
> +                i++;
> +            }
> +            if (!found)
> +                ereport(ERROR,
> +                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> +                         errmsg("unrecognized parameter namespace \"%s\"",
> +                                def->defnamespace)));
> +        }
> +    }
> +}
> +
> +/*
> + * optionsDefListFilterNamespaces
> + *        Iterates over DefList, choose items with specified namespace and adds
> + *        them to a result List
> + *
> + * This function does not destroy source DefList but does not create copies
> + * of List nodes.
> + * It is actually used only for tables, in order to split toast and heap
> + * reloptions, so each one can be stored in on it's own pg_class record
> + */
> +List *
> +optionsDefListFilterNamespaces(List *defList, const char *namespace)
> +{
> +    ListCell   *cell;
> +    List       *result = NIL;
> +
> +    foreach(cell, defList)
> +    {
> +        DefElem    *def = (DefElem *) lfirst(cell);
> +
> +        if ((!namespace && !def->defnamespace) ||
> +            (namespace && def->defnamespace &&
> +             strcmp(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;
> +}
> +
> +/* FIXME write comment here */
> +
> +Datum
> +optionsDefListToTextArray(List *defList)
> +{
> +    ListCell   *cell;
> +    Datum        result;
> +    ArrayBuildState *astate = NULL;
> +
> +    foreach(cell, defList)
> +    {
> +        DefElem       *def = (DefElem *) lfirst(cell);
> +        const char *name = def->defname;
> +        const char *value;
> +        text       *t;
> +        int            len;
> +
> +        if (def->arg != NULL)
> +            value = defGetString(def);
> +        else
> +            value = "true";
> +
> +        if (def->defnamespace)
> +        {
> +            Assert(false); /* Should not get here */
> +            /* This function is used for backward compatibility in the place were namespases are not allowed */
> +            return (Datum) 0;
> +        }
> +        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;
> +}
> +
> +
> +/*
> + * 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_spec_set 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_spec_set 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_spec_set * spec_set,
> +                      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) * spec_set->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 && (!spec_set->namespace ||
> +                  strcmp(spec_set->namespace, option->namespace) != 0))
> +        {
> +            ereport(ERROR,
> +                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> +                     errmsg("unrecognized parameter namespace \"%s\"",
> +                            option->namespace)));
> +        }
> +
> +        for (i = 0; i < spec_set->num; i++)
> +        {
> +            option_spec_basic *definition = spec_set->definitions[i];
> +
> +            if (strcmp(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 spec_set. 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)));
> +            } else
> +            {
> +                /* RESET is always in non-validating mode, unkown names should
> +                 * be ignored. This is traditional behaviour of postgres/
> +                 * FIXME may be it should be changed someday
> +                 */
> +                if (option->status == OPTION_VALUE_STATUS_FOR_RESET)
> +                {
> +                    skip = true;
> +                }
> +            }
> +            /*
> +             * In other cases, if we are parsing not in validate mode, then
> +             * we should keep unknown node, because non-validate mode is for
> +             * data that is already in the DB and should not be changed after
> +             * altering another entries
> +             */
> +        }
> +        if (!skip)
> +            result = lappend(result, option);
> +    }
> +    return result;
> +}
> +
> +/*
> + * parse_one_option
> + *
> + *        Subroutine for optionsParseRawValues, to parse and validate a
> + *        single option's value
> + */
> +static void
> +parse_one_option(option_value * option, const char *text_str, int text_len,
> +                 bool validate)
> +{
> +    char       *value;
> +    bool        parsed;
> +
> +    value = option->raw_value;
> +
> +    switch (option->gen->type)
> +    {
> +        case OPTION_TYPE_BOOL:
> +            {
> +                parsed = parse_bool(value, &option->values.bool_val);
> +                if (validate && !parsed)
> +                    ereport(ERROR,
> +                            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> +                        errmsg("invalid value for boolean option \"%s\": %s",
> +                               option->gen->name, value)));
> +            }
> +            break;
> +        case OPTION_TYPE_INT:
> +            {
> +                option_spec_int *optint =
> +                (option_spec_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_spec_real *optreal =
> +                (option_spec_real *) option->gen;
> +
> +                parsed = parse_real(value, &option->values.real_val, 0, NULL);
> +                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_spec_enum *optenum =
> +                                        (option_spec_enum *) option->gen;
> +                opt_enum_elt_def *elt;
> +                parsed = false;
> +                for (elt = optenum->members; elt->string_val; elt++)
> +                {
> +                    if (strcmp(value, elt->string_val) == 0)
> +                    {
> +                        option->values.enum_val = elt->symbol_val;
> +                        parsed = true;
> +                        break;
> +                    }
> +                }
> +                if (!parsed)
> +                {
> +                    ereport(ERROR,
> +                            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> +                             errmsg("invalid value for enum option \"%s\": %s",
> +                                    option->gen->name, value),
> +                             optenum->detailmsg ?
> +                             errdetail_internal("%s", _(optenum->detailmsg)) : 0));
> +                }
> +            }
> +            break;
> +        case OPTION_TYPE_STRING:
> +            {
> +                option_spec_string *optstring =
> +                (option_spec_string *) option->gen;
> +
> +                option->values.string_val = value;
> +                if (validate && optstring->validate_cb)
> +                    (optstring->validate_cb) (value);
> +                parsed = true;
> +            }
> +            break;
> +        default:
> +            elog(ERROR, "unsupported reloption type %d", option->gen->type);
> +            parsed = true;        /* quiet compiler */
> +            break;
> +    }
> +
> +    if (parsed)
> +        option->status = OPTION_VALUE_STATUS_PARSED;
> +
> +}
> +
> +/*
> + * optionsAllocateBytea
> + *        Allocates memory for bytea options representation
> + *
> + * Function allocates memory for byrea structure of an option, plus adds space
> + * for values of string options. We should keep all data including string
> + * values in the same memory chunk, because Cache code copies bytea option
> + * data from one MemoryConext to another without knowing about it's internal
> + * structure, so it would not be able to copy string values if they are outside
> + * of bytea memory chunk.
> + */
> +static void *
> +optionsAllocateBytea(options_spec_set * spec_set, List *options)
> +{
> +    Size        size;
> +    int            i;
> +    ListCell   *cell;
> +    int            length;
> +    void       *res;
> +
> +    size = spec_set->struct_size;
> +
> +    /* Calculate size needed to store all string values for this option */
> +    for (i = 0; i < spec_set->num; i++)
> +    {
> +        option_spec_basic *definition = spec_set->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 spec_set
> +         * entry
> +         */
> +        foreach(cell, options)
> +        {
> +            option = (option_value *) lfirst(cell);
> +            if (option->status == OPTION_VALUE_STATUS_PARSED &&
> +                strcmp(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_spec_string *) definition)->default_val)
> +            length = strlen(
> +                 ((option_spec_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_spec_set: each spec_set item
> + * has offset value, and option value in binary mode is written to the
> + * structure with that offset.
> + *
> + * More special case is string values. Memory for bytea structure is allocated
> + * by optionsAllocateBytea which adds some more space for string values to
> + * the size of original structure. All string values are copied there and
> + * inside the bytea structure an offset to that value is kept.
> + *
> + */
> +static bytea *
> +optionsValuesToBytea(List *options, options_spec_set * spec_set)
> +{
> +    char       *data;
> +    char       *string_values_buffer;
> +    int            i;
> +
> +    data = optionsAllocateBytea(spec_set, options);
> +
> +    /* place for string data starts right after original structure */
> +    string_values_buffer = data + spec_set->struct_size;
> +
> +    for (i = 0; i < spec_set->num; i++)
> +    {
> +        option_value *found = NULL;
> +        ListCell   *cell;
> +        char       *item_pos;
> +        option_spec_basic *definition = spec_set->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 (strcmp(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_spec_bool *) definition)->default_val;
> +                break;
> +            case OPTION_TYPE_INT:
> +                *(int *) item_pos = found ?
> +                    found->values.int_val :
> +                    ((option_spec_int *) definition)->default_val;
> +                break;
> +            case OPTION_TYPE_REAL:
> +                *(double *) item_pos = found ?
> +                    found->values.real_val :
> +                    ((option_spec_real *) definition)->default_val;
> +                break;
> +            case OPTION_TYPE_ENUM:
> +                *(int *) item_pos = found ?
> +                    found->values.enum_val :
> +                    ((option_spec_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_spec_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_spec_set * spec_set, 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);
> +    if (! (parse_mode & OPTIONS_PARSE_MODE_FOR_RESET))
> +    {
> +        /* FIXME: postgres usual behaviour vas not to vaidate names that
> +         * came from RESET command. Once this behavious should be changed,
> +         * I guess. But for now we keep it as it was.
> +         */
> +        parse_mode|= OPTIONS_PARSE_MODE_VALIDATE;
> +    }
> +    new_values = optionsParseRawValues(new_values, spec_set, parse_mode);
> +
> +    /*
> +     * 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, spec_set, 0);
> +    }
> +    else
> +    {
> +        merged_values = new_values;
> +    }
> +
> +    /*
> +     * If we have postprocess_fun function defined in spec_set, 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 (spec_set->postprocess_fun)
> +    {
> +        bytea       *data = optionsValuesToBytea(merged_values, spec_set);
> +
> +        spec_set->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_spec_set * spec_set, Datum data, bool validate)
> +{
> +    List       *values;
> +    bytea       *options;
> +
> +    values = optionsTextArrayToRawValues(data);
> +    values = optionsParseRawValues(values, spec_set,
> +                                validate ? OPTIONS_PARSE_MODE_VALIDATE : 0);
> +    options = optionsValuesToBytea(values, spec_set);
> +
> +    if (spec_set->postprocess_fun)
> +    {
> +        spec_set->postprocess_fun(options, false);
> +    }
> +    return options;
> +}
> diff --git a/src/backend/access/common/relation.c b/src/backend/access/common/relation.c
> index 632d13c..49ad197 100644
> --- a/src/backend/access/common/relation.c
> +++ b/src/backend/access/common/relation.c
> @@ -65,9 +65,13 @@ relation_open(Oid relationId, LOCKMODE lockmode)
>       * If we didn't get the lock ourselves, assert that caller holds one,
>       * except in bootstrap mode where no locks are used.
>       */
> -    Assert(lockmode != NoLock ||
> -           IsBootstrapProcessingMode() ||
> -           CheckRelationLockedByMe(r, AccessShareLock, true));
> +
> +// FIXME We need NoLock mode to get AM data when choosing Lock for
> +// attoptions is changed. See ProcessUtilitySlow problems comes from there
> +// This is a dirty hack, we need better solution for this case;
> +//    Assert(lockmode != NoLock ||
> +//           IsBootstrapProcessingMode() ||
> +//           CheckRelationLockedByMe(r, AccessShareLock, true));
>  
>      /* Make note that we've accessed a temporary relation */
>      if (RelationUsesLocalBuffers(r))
> diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
> index b5602f5..29ab98a 100644
> --- a/src/backend/access/common/reloptions.c
> +++ b/src/backend/access/common/reloptions.c
> @@ -1,7 +1,7 @@
>  /*-------------------------------------------------------------------------
>   *
>   * reloptions.c
> - *      Core support for relation options (pg_class.reloptions)
> + *      Support for relation options (pg_class.reloptions)
>   *
>   * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
>   * Portions Copyright (c) 1994, Regents of the University of California
> @@ -17,13 +17,10 @@
>  
>  #include <float.h>
>  
> -#include "access/gist_private.h"
> -#include "access/hash.h"
>  #include "access/heaptoast.h"
>  #include "access/htup_details.h"
> -#include "access/nbtree.h"
>  #include "access/reloptions.h"
> -#include "access/spgist_private.h"
> +#include "access/options.h"
>  #include "catalog/pg_type.h"
>  #include "commands/defrem.h"
>  #include "commands/tablespace.h"
> @@ -36,6 +33,7 @@
>  #include "utils/guc.h"
>  #include "utils/memutils.h"
>  #include "utils/rel.h"
> +#include "storage/bufmgr.h"
>  
>  /*
>   * Contents of pg_class.reloptions
> @@ -93,380 +91,8 @@
>   * value has no effect until the next VACUUM, so no need for stronger lock.
>   */
>  
> -static relopt_bool boolRelOpts[] =
> -{
> -    {
> -        {
> -            "autosummarize",
> -            "Enables automatic summarization on this BRIN index",
> -            RELOPT_KIND_BRIN,
> -            AccessExclusiveLock
> -        },
> -        false
> -    },
> -    {
> -        {
> -            "autovacuum_enabled",
> -            "Enables autovacuum in this relation",
> -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> -            ShareUpdateExclusiveLock
> -        },
> -        true
> -    },
> -    {
> -        {
> -            "user_catalog_table",
> -            "Declare a table as an additional catalog table, e.g. for the purpose of logical replication",
> -            RELOPT_KIND_HEAP,
> -            AccessExclusiveLock
> -        },
> -        false
> -    },
> -    {
> -        {
> -            "fastupdate",
> -            "Enables \"fast update\" feature for this GIN index",
> -            RELOPT_KIND_GIN,
> -            AccessExclusiveLock
> -        },
> -        true
> -    },
> -    {
> -        {
> -            "security_barrier",
> -            "View acts as a row security barrier",
> -            RELOPT_KIND_VIEW,
> -            AccessExclusiveLock
> -        },
> -        false
> -    },
> -    {
> -        {
> -            "vacuum_truncate",
> -            "Enables vacuum to truncate empty pages at the end of this table",
> -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> -            ShareUpdateExclusiveLock
> -        },
> -        true
> -    },
> -    {
> -        {
> -            "deduplicate_items",
> -            "Enables \"deduplicate items\" feature for this btree index",
> -            RELOPT_KIND_BTREE,
> -            ShareUpdateExclusiveLock    /* since it applies only to later
> -                                         * inserts */
> -        },
> -        true
> -    },
> -    /* 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_vacuum_insert_threshold",
> -            "Minimum number of tuple inserts prior to vacuum, or -1 to disable insert vacuums",
> -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> -            ShareUpdateExclusiveLock
> -        },
> -        -2, -1, 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_limit",
> -            "Vacuum cost amount available before napping, for autovacuum",
> -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> -            ShareUpdateExclusiveLock
> -        },
> -        -1, 1, 10000
> -    },
> -    {
> -        {
> -            "autovacuum_freeze_min_age",
> -            "Minimum age at which VACUUM should freeze a table row, for autovacuum",
> -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> -            ShareUpdateExclusiveLock
> -        },
> -        -1, 0, 1000000000
> -    },
> -    {
> -        {
> -            "autovacuum_multixact_freeze_min_age",
> -            "Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
> -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> -            ShareUpdateExclusiveLock
> -        },
> -        -1, 0, 1000000000
> -    },
> -    {
> -        {
> -            "autovacuum_freeze_max_age",
> -            "Age at which to autovacuum a table to prevent transaction ID wraparound",
> -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> -            ShareUpdateExclusiveLock
> -        },
> -        -1, 100000, 2000000000
> -    },
> -    {
> -        {
> -            "autovacuum_multixact_freeze_max_age",
> -            "Multixact age at which to autovacuum a table to prevent multixact wraparound",
> -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> -            ShareUpdateExclusiveLock
> -        },
> -        -1, 10000, 2000000000
> -    },
> -    {
> -        {
> -            "autovacuum_freeze_table_age",
> -            "Age at which VACUUM should perform a full table sweep to freeze row versions",
> -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> -            ShareUpdateExclusiveLock
> -        }, -1, 0, 2000000000
> -    },
> -    {
> -        {
> -            "autovacuum_multixact_freeze_table_age",
> -            "Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
> -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> -            ShareUpdateExclusiveLock
> -        }, -1, 0, 2000000000
> -    },
> -    {
> -        {
> -            "log_autovacuum_min_duration",
> -            "Sets the minimum execution time above which autovacuum actions will be logged",
> -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> -            ShareUpdateExclusiveLock
> -        },
> -        -1, -1, INT_MAX
> -    },
> -    {
> -        {
> -            "toast_tuple_target",
> -            "Sets the target tuple length at which external columns will be toasted",
> -            RELOPT_KIND_HEAP,
> -            ShareUpdateExclusiveLock
> -        },
> -        TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
> -    },
> -    {
> -        {
> -            "pages_per_range",
> -            "Number of pages that each page range covers in a BRIN index",
> -            RELOPT_KIND_BRIN,
> -            AccessExclusiveLock
> -        }, 128, 1, 131072
> -    },
> -    {
> -        {
> -            "gin_pending_list_limit",
> -            "Maximum size of the pending list for this GIN index, in kilobytes.",
> -            RELOPT_KIND_GIN,
> -            AccessExclusiveLock
> -        },
> -        -1, 64, MAX_KILOBYTES
> -    },
> -    {
> -        {
> -            "effective_io_concurrency",
> -            "Number of simultaneous requests that can be handled efficiently by the disk subsystem.",
> -            RELOPT_KIND_TABLESPACE,
> -            ShareUpdateExclusiveLock
> -        },
> -#ifdef USE_PREFETCH
> -        -1, 0, MAX_IO_CONCURRENCY
> -#else
> -        0, 0, 0
> -#endif
> -    },
> -    {
> -        {
> -            "maintenance_io_concurrency",
> -            "Number of simultaneous requests that can be handled efficiently by the disk subsystem for maintenance
work.",
> -            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_cost_delay",
> -            "Vacuum cost delay in milliseconds, for autovacuum",
> -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> -            ShareUpdateExclusiveLock
> -        },
> -        -1, 0.0, 100.0
> -    },
> -    {
> -        {
> -            "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_vacuum_insert_scale_factor",
> -            "Number of tuple inserts 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
> -    },
> -    {
> -        {
> -            "vacuum_cleanup_index_scale_factor",
> -            "Deprecated B-Tree parameter.",
> -            RELOPT_KIND_BTREE,
> -            ShareUpdateExclusiveLock
> -        },
> -        -1, 0.0, 1e10
> -    },
> -    /* list terminator */
> -    {{NULL}}
> -};
> -
>  /* values from StdRdOptIndexCleanup */
> -relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
> +opt_enum_elt_def StdRdOptIndexCleanupValues[] =
>  {
>      {"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
>      {"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
> @@ -480,17 +106,8 @@ relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
>      {(const char *) NULL}        /* list terminator */
>  };
>  
> -/* values from GistOptBufferingMode */
> -relopt_enum_elt_def gistBufferingOptValues[] =
> -{
> -    {"auto", GIST_OPTION_BUFFERING_AUTO},
> -    {"on", GIST_OPTION_BUFFERING_ON},
> -    {"off", GIST_OPTION_BUFFERING_OFF},
> -    {(const char *) NULL}        /* list terminator */
> -};
> -
>  /* values from ViewOptCheckOption */
> -relopt_enum_elt_def viewCheckOptValues[] =
> +opt_enum_elt_def viewCheckOptValues[] =
>  {
>      /* no value for NOT_SET */
>      {"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
> @@ -498,61 +115,8 @@ relopt_enum_elt_def viewCheckOptValues[] =
>      {(const char *) NULL}        /* list terminator */
>  };
>  
> -static relopt_enum enumRelOpts[] =
> -{
> -    {
> -        {
> -            "vacuum_index_cleanup",
> -            "Controls index vacuuming and index cleanup",
> -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> -            ShareUpdateExclusiveLock
> -        },
> -        StdRdOptIndexCleanupValues,
> -        STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO,
> -        gettext_noop("Valid values are \"on\", \"off\", and \"auto\".")
> -    },
> -    {
> -        {
> -            "buffering",
> -            "Enables buffering build for this GiST index",
> -            RELOPT_KIND_GIST,
> -            AccessExclusiveLock
> -        },
> -        gistBufferingOptValues,
> -        GIST_OPTION_BUFFERING_AUTO,
> -        gettext_noop("Valid values are \"on\", \"off\", and \"auto\".")
> -    },
> -    {
> -        {
> -            "check_option",
> -            "View has WITH CHECK OPTION defined (local or cascaded).",
> -            RELOPT_KIND_VIEW,
> -            AccessExclusiveLock
> -        },
> -        viewCheckOptValues,
> -        VIEW_OPTION_CHECK_OPTION_NOT_SET,
> -        gettext_noop("Valid values are \"local\" and \"cascaded\".")
> -    },
> -    /* list terminator */
> -    {{NULL}}
> -};
> -
> -static relopt_string stringRelOpts[] =
> -{
> -    /* 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);
> +options_spec_set *get_stdrd_relopt_spec_set(relopt_kind kind);
>  
>  /*
>   * Get the length of a string reloption (either default or the user-defined
> @@ -563,160 +127,6 @@ static void parse_one_reloption(relopt_value *option, char *text_str,
>      ((option).isset ? strlen((option).values.string_val) : \
>       ((relopt_string *) (option).gen)->default_len)
>  
> -/*
> - * 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; enumRelOpts[i].gen.name; i++)
> -    {
> -        Assert(DoLockModesConflict(enumRelOpts[i].gen.lockmode,
> -                                   enumRelOpts[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; enumRelOpts[i].gen.name; i++)
> -    {
> -        relOpts[j] = &enumRelOpts[i].gen;
> -        relOpts[j]->type = RELOPT_TYPE_ENUM;
> -        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;
> -}
>  
>  /*
>   * init_local_reloptions
> @@ -729,6 +139,7 @@ init_local_reloptions(local_relopts *opts, Size relopt_struct_size)
>      opts->options = NIL;
>      opts->validators = NIL;
>      opts->relopt_struct_size = relopt_struct_size;
> +    opts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, 0);
>  }
>  
>  /*
> @@ -743,112 +154,6 @@ register_reloptions_validator(local_relopts *opts, relopts_validator validator)
>  }
>  
>  /*
> - * add_local_reloption
> - *        Add an already-created custom reloption to the local list.
> - */
> -static void
> -add_local_reloption(local_relopts *relopts, relopt_gen *newoption, int offset)
> -{
> -    local_relopt *opt = palloc(sizeof(*opt));
> -
> -    Assert(offset < relopts->relopt_struct_size);
> -
> -    opt->option = newoption;
> -    opt->offset = offset;
> -
> -    relopts->options = lappend(relopts->options, opt);
> -}
> -
> -/*
> - * allocate_reloption
> - *        Allocate a new reloption and initialize the type-agnostic fields
> - *        (for types other than string)
> - */
> -static relopt_gen *
> -allocate_reloption(bits32 kinds, int type, const char *name, const char *desc,
> -                   LOCKMODE lockmode)
> -{
> -    MemoryContext oldcxt;
> -    size_t        size;
> -    relopt_gen *newoption;
> -
> -    if (kinds != RELOPT_KIND_LOCAL)
> -        oldcxt = MemoryContextSwitchTo(TopMemoryContext);
> -    else
> -        oldcxt = NULL;
> -
> -    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_ENUM:
> -            size = sizeof(relopt_enum);
> -            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;
> -    newoption->lockmode = lockmode;
> -
> -    if (oldcxt != NULL)
> -        MemoryContextSwitchTo(oldcxt);
> -
> -    return newoption;
> -}
> -
> -/*
> - * init_bool_reloption
> - *        Allocate and initialize a new boolean reloption
> - */
> -static relopt_bool *
> -init_bool_reloption(bits32 kinds, const char *name, const char *desc,
> -                    bool default_val, LOCKMODE lockmode)
> -{
> -    relopt_bool *newoption;
> -
> -    newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
> -                                                   name, desc, lockmode);
> -    newoption->default_val = default_val;
> -
> -    return newoption;
> -}
> -
> -/*
> - * add_bool_reloption
> - *        Add a new boolean reloption
> - */
> -void
> -add_bool_reloption(bits32 kinds, const char *name, const char *desc,
> -                   bool default_val, LOCKMODE lockmode)
> -{
> -    relopt_bool *newoption = init_bool_reloption(kinds, name, desc,
> -                                                 default_val, lockmode);
> -
> -    add_reloption((relopt_gen *) newoption);
> -}
> -
> -/*
>   * add_local_bool_reloption
>   *        Add a new boolean local reloption
>   *
> @@ -858,47 +163,8 @@ void
>  add_local_bool_reloption(local_relopts *relopts, const char *name,
>                           const char *desc, bool default_val, int offset)
>  {
> -    relopt_bool *newoption = init_bool_reloption(RELOPT_KIND_LOCAL,
> -                                                 name, desc,
> -                                                 default_val, 0);
> -
> -    add_local_reloption(relopts, (relopt_gen *) newoption, offset);
> -}
> -
> -
> -/*
> - * init_real_reloption
> - *        Allocate and initialize a new integer reloption
> - */
> -static relopt_int *
> -init_int_reloption(bits32 kinds, const char *name, const char *desc,
> -                   int default_val, int min_val, int max_val,
> -                   LOCKMODE lockmode)
> -{
> -    relopt_int *newoption;
> -
> -    newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
> -                                                  name, desc, lockmode);
> -    newoption->default_val = default_val;
> -    newoption->min = min_val;
> -    newoption->max = max_val;
> -
> -    return newoption;
> -}
> -
> -/*
> - * add_int_reloption
> - *        Add a new integer reloption
> - */
> -void
> -add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
> -                  int min_val, int max_val, LOCKMODE lockmode)
> -{
> -    relopt_int *newoption = init_int_reloption(kinds, name, desc,
> -                                               default_val, min_val,
> -                                               max_val, lockmode);
> -
> -    add_reloption((relopt_gen *) newoption);
> +    optionsSpecSetAddBool(relopts->spec_set, name, desc, NoLock, 0, offset,
> +                                                                default_val);
>  }
>  
>  /*
> @@ -912,47 +178,8 @@ add_local_int_reloption(local_relopts *relopts, const char *name,
>                          const char *desc, int default_val, int min_val,
>                          int max_val, int offset)
>  {
> -    relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
> -                                               name, desc, default_val,
> -                                               min_val, max_val, 0);
> -
> -    add_local_reloption(relopts, (relopt_gen *) newoption, offset);
> -}
> -
> -/*
> - * init_real_reloption
> - *        Allocate and initialize a new real reloption
> - */
> -static relopt_real *
> -init_real_reloption(bits32 kinds, const char *name, const char *desc,
> -                    double default_val, double min_val, double max_val,
> -                    LOCKMODE lockmode)
> -{
> -    relopt_real *newoption;
> -
> -    newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
> -                                                   name, desc, lockmode);
> -    newoption->default_val = default_val;
> -    newoption->min = min_val;
> -    newoption->max = max_val;
> -
> -    return newoption;
> -}
> -
> -/*
> - * add_real_reloption
> - *        Add a new float reloption
> - */
> -void
> -add_real_reloption(bits32 kinds, const char *name, const char *desc,
> -                   double default_val, double min_val, double max_val,
> -                   LOCKMODE lockmode)
> -{
> -    relopt_real *newoption = init_real_reloption(kinds, name, desc,
> -                                                 default_val, min_val,
> -                                                 max_val, lockmode);
> -
> -    add_reloption((relopt_gen *) newoption);
> +    optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, 0, offset,
> +                                                default_val, min_val, max_val);
>  }
>  
>  /*
> @@ -966,57 +193,9 @@ add_local_real_reloption(local_relopts *relopts, const char *name,
>                           const char *desc, double default_val,
>                           double min_val, double max_val, int offset)
>  {
> -    relopt_real *newoption = init_real_reloption(RELOPT_KIND_LOCAL,
> -                                                 name, desc,
> -                                                 default_val, min_val,
> -                                                 max_val, 0);
> -
> -    add_local_reloption(relopts, (relopt_gen *) newoption, offset);
> -}
> -
> -/*
> - * init_enum_reloption
> - *        Allocate and initialize a new enum reloption
> - */
> -static relopt_enum *
> -init_enum_reloption(bits32 kinds, const char *name, const char *desc,
> -                    relopt_enum_elt_def *members, int default_val,
> -                    const char *detailmsg, LOCKMODE lockmode)
> -{
> -    relopt_enum *newoption;
> -
> -    newoption = (relopt_enum *) allocate_reloption(kinds, RELOPT_TYPE_ENUM,
> -                                                   name, desc, lockmode);
> -    newoption->members = members;
> -    newoption->default_val = default_val;
> -    newoption->detailmsg = detailmsg;
> -
> -    return newoption;
> -}
> -
> -
> -/*
> - * add_enum_reloption
> - *        Add a new enum reloption
> - *
> - * The members array must have a terminating NULL entry.
> - *
> - * The detailmsg is shown when unsupported values are passed, and has this
> - * form:   "Valid values are \"foo\", \"bar\", and \"bar\"."
> - *
> - * The members array and detailmsg are not copied -- caller must ensure that
> - * they are valid throughout the life of the process.
> - */
> -void
> -add_enum_reloption(bits32 kinds, const char *name, const char *desc,
> -                   relopt_enum_elt_def *members, int default_val,
> -                   const char *detailmsg, LOCKMODE lockmode)
> -{
> -    relopt_enum *newoption = init_enum_reloption(kinds, name, desc,
> -                                                 members, default_val,
> -                                                 detailmsg, lockmode);
> +    optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, 0, offset,
> +                                                default_val, min_val, max_val);
>  
> -    add_reloption((relopt_gen *) newoption);
>  }
>  
>  /*
> @@ -1027,77 +206,11 @@ add_enum_reloption(bits32 kinds, const char *name, const char *desc,
>   */
>  void
>  add_local_enum_reloption(local_relopts *relopts, const char *name,
> -                         const char *desc, relopt_enum_elt_def *members,
> +                         const char *desc, opt_enum_elt_def *members,
>                           int default_val, const char *detailmsg, int offset)
>  {
> -    relopt_enum *newoption = init_enum_reloption(RELOPT_KIND_LOCAL,
> -                                                 name, desc,
> -                                                 members, default_val,
> -                                                 detailmsg, 0);
> -
> -    add_local_reloption(relopts, (relopt_gen *) newoption, offset);
> -}
> -
> -/*
> - * init_string_reloption
> - *        Allocate and initialize a new string reloption
> - */
> -static relopt_string *
> -init_string_reloption(bits32 kinds, const char *name, const char *desc,
> -                      const char *default_val,
> -                      validate_string_relopt validator,
> -                      fill_string_relopt filler,
> -                      LOCKMODE lockmode)
> -{
> -    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, lockmode);
> -    newoption->validate_cb = validator;
> -    newoption->fill_cb = filler;
> -    if (default_val)
> -    {
> -        if (kinds == RELOPT_KIND_LOCAL)
> -            newoption->default_val = strdup(default_val);
> -        else
> -            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;
> -    }
> -
> -    return newoption;
> -}
> -
> -/*
> - * add_string_reloption
> - *        Add a new string reloption
> - *
> - * "validator" is an optional function pointer that can be used to test the
> - * validity of the values.  It must elog(ERROR) when the argument string is
> - * not acceptable for the variable.  Note that the default value must pass
> - * the validation.
> - */
> -void
> -add_string_reloption(bits32 kinds, const char *name, const char *desc,
> -                     const char *default_val, validate_string_relopt validator,
> -                     LOCKMODE lockmode)
> -{
> -    relopt_string *newoption = init_string_reloption(kinds, name, desc,
> -                                                     default_val,
> -                                                     validator, NULL,
> -                                                     lockmode);
> -
> -    add_reloption((relopt_gen *) newoption);
> +    optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, 0, offset,
> +                                            members, default_val, detailmsg);
>  }
>  
>  /*
> @@ -1113,249 +226,9 @@ add_local_string_reloption(local_relopts *relopts, const char *name,
>                             validate_string_relopt validator,
>                             fill_string_relopt filler, int offset)
>  {
> -    relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
> -                                                     name, desc,
> -                                                     default_val,
> -                                                     validator, filler,
> -                                                     0);
> -
> -    add_local_reloption(relopts, (relopt_gen *) newoption, offset);
> -}
> -
> -/*
> - * 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 acceptOidsOff is true, then we allow oids = false, but throw error when
> - * on. This is solely needed for backwards compatibility.
> - *
> - * Note that this is not responsible for determining whether the options
> - * are valid, but it does check that namespaces for all the options given are
> - * listed in validnsps.  The NULL namespace is always valid and need not be
> - * explicitly listed.  Passing a NULL pointer means that only the NULL
> - * namespace is valid.
> - *
> - * Both oldOptions and the result are text arrays (or NULL for "default"),
> - * but we declare them as Datums to avoid including array.h in reloptions.h.
> - */
> -Datum
> -transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
> -                    char *validnsps[], bool acceptOidsOff, 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, TYPALIGN_INT,
> -                          &oldoptions, NULL, &noldoptions);
> -
> -        for (i = 0; i < noldoptions; i++)
> -        {
> -            char       *text_str = VARDATA(oldoptions[i]);
> -            int            text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
> -
> -            /* Search for a match in defList */
> -            foreach(cell, defList)
> -            {
> -                DefElem    *def = (DefElem *) lfirst(cell);
> -                int            kw_len;
> -
> -                /* ignore if not in the same namespace */
> -                if (namspace == NULL)
> -                {
> -                    if (def->defnamespace != NULL)
> -                        continue;
> -                }
> -                else if (def->defnamespace == NULL)
> -                    continue;
> -                else if (strcmp(def->defnamespace, namspace) != 0)
> -                    continue;
> -
> -                kw_len = strlen(def->defname);
> -                if (text_len > kw_len && text_str[kw_len] == '=' &&
> -                    strncmp(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 (strcmp(def->defnamespace, validnsps[i]) == 0)
> -                        {
> -                            valid = true;
> -                            break;
> -                        }
> -                    }
> -                }
> -
> -                if (!valid)
> -                    ereport(ERROR,
> -                            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> -                             errmsg("unrecognized parameter namespace \"%s\"",
> -                                    def->defnamespace)));
> -            }
> -
> -            /* ignore if not in the same namespace */
> -            if (namspace == NULL)
> -            {
> -                if (def->defnamespace != NULL)
> -                    continue;
> -            }
> -            else if (def->defnamespace == NULL)
> -                continue;
> -            else if (strcmp(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";
> -
> -            /*
> -             * This is not a great place for this test, but there's no other
> -             * convenient place to filter the option out. As WITH (oids =
> -             * false) will be removed someday, this seems like an acceptable
> -             * amount of ugly.
> -             */
> -            if (acceptOidsOff && def->defnamespace == NULL &&
> -                strcmp(def->defname, "oids") == 0)
> -            {
> -                if (defGetBoolean(def))
> -                    ereport(ERROR,
> -                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
> -                             errmsg("tables declared WITH OIDS are not supported")));
> -                /* skip over option, reloptions machinery doesn't know it */
> -                continue;
> -            }
> -
> -            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, TYPALIGN_INT,
> -                      &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;
> +    optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, 0, offset,
> +                                            default_val, validator);
> +/* FIXME solve mistery with filler option! */
>  }
>  
>  /*
> @@ -1372,12 +245,13 @@ untransformRelOptions(Datum options)
>   */
>  bytea *
>  extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
> -                  amoptions_function amoptions)
> +                  amreloptspecset_function amoptionsspecsetfn)
>  {
>      bytea       *options;
>      bool        isnull;
>      Datum        datum;
>      Form_pg_class classForm;
> +    options_spec_set *spec_set;
>  
>      datum = fastgetattr(tuple,
>                          Anum_pg_class_reloptions,
> @@ -1394,702 +268,341 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
>          case RELKIND_RELATION:
>          case RELKIND_TOASTVALUE:
>          case RELKIND_MATVIEW:
> -            options = heap_reloptions(classForm->relkind, datum, false);
> +            spec_set = get_heap_relopt_spec_set();
>              break;
>          case RELKIND_PARTITIONED_TABLE:
> -            options = partitioned_table_reloptions(datum, false);
> +            spec_set = get_partitioned_relopt_spec_set();
>              break;
>          case RELKIND_VIEW:
> -            options = view_reloptions(datum, false);
> +            spec_set = get_view_relopt_spec_set();
>              break;
>          case RELKIND_INDEX:
>          case RELKIND_PARTITIONED_INDEX:
> -            options = index_reloptions(amoptions, datum, false);
> +            if (amoptionsspecsetfn)
> +                spec_set = amoptionsspecsetfn();
> +            else
> +                spec_set = NULL;
>              break;
>          case RELKIND_FOREIGN_TABLE:
> -            options = NULL;
> +            spec_set = NULL;
>              break;
>          default:
>              Assert(false);        /* can't get here */
> -            options = NULL;        /* keep compiler quiet */
> +            spec_set = NULL;        /* keep compiler quiet */
>              break;
>      }
> +    if (spec_set)
> +        options = optionsTextArrayToBytea(spec_set, datum, 0);
> +    else
> +        options = NULL;
>  
>      return options;
>  }
>  
> -static void
> -parseRelOptionsInternal(Datum options, bool validate,
> -                        relopt_value *reloptions, int numoptions)
> -{
> -    ArrayType  *array = DatumGetArrayTypeP(options);
> -    Datum       *optiondatums;
> -    int            noptions;
> -    int            i;
> -
> -    deconstruct_array(array, TEXTOID, -1, false, TYPALIGN_INT,
> -                      &optiondatums, NULL, &noptions);
> +options_spec_set *
> +get_stdrd_relopt_spec_set(relopt_kind kind)
> +{
> +    bool is_for_toast = (kind == RELOPT_KIND_TOAST);
> +
> +    options_spec_set * stdrd_relopt_spec_set = allocateOptionsSpecSet(
> +                    is_for_toast ? "toast" : NULL,  sizeof(StdRdOptions), 0); //FIXME change 0 to actual value (may
be)
> +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "fillfactor",
> +                                 "Packs table pages only to this percentag",
> +                                 ShareUpdateExclusiveLock,        /* since it applies only
> +                                                                 * to later inserts */
> +                                is_for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0,
> +                                offsetof(StdRdOptions, fillfactor),
> +                          HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100);
> +    optionsSpecSetAddBool(stdrd_relopt_spec_set, "autovacuum_enabled",
> +                              "Enables autovacuum in this relation",
> +                              ShareUpdateExclusiveLock, 0,
> +            offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled),
> +                              true);
> +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_vacuum_threshold",
> +                "Minimum number of tuple updates or deletes prior to vacuum",
> +                             ShareUpdateExclusiveLock,
> +                    0, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold),
> +                             -1, 0, INT_MAX);
> +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_analyze_threshold",
> +                "Minimum number of tuple updates or deletes prior to vacuum",
> +                             ShareUpdateExclusiveLock,
> +                             is_for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0,
> +                      offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold),
> +                             -1, 0, INT_MAX);
> +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_vacuum_cost_limit",
> +               "Vacuum cost amount available before napping, for autovacuum",
> +                             ShareUpdateExclusiveLock,
> +                   0, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit),
> +                             -1, 0, 10000);
> +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_freeze_min_age",
> +     "Minimum age at which VACUUM should freeze a table row, for autovacuum",
> +                             ShareUpdateExclusiveLock,
> +                      0, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age),
> +                             -1, 0, 1000000000);
> +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_freeze_max_age",
> +    "Age at which to autovacuum a table to prevent transaction ID wraparound",
> +                             ShareUpdateExclusiveLock,
> +                      0, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age),
> +                             -1, 100000, 2000000000);
> +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_freeze_table_age",
> +                             "Age at which VACUUM should perform a full table sweep to freeze row versions",
> +                             ShareUpdateExclusiveLock,
> +                    0, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age),
> +                             -1, 0, 2000000000);
> +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_multixact_freeze_min_age",
> +                             "Minimum multixact age at which VACUUM should freeze a row multixact's, for
autovacuum",
> +                             ShareUpdateExclusiveLock,
> +            0, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age),
> +                             -1, 0, 1000000000);
> +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_multixact_freeze_max_age",
> +                             "Multixact age at which to autovacuum a table to prevent multixact wraparound",
> +                             ShareUpdateExclusiveLock,
> +            0, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age),
> +                             -1, 10000, 2000000000);
> +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_multixact_freeze_table_age",
> +                             "Age of multixact at which VACUUM should perform a full table sweep to freeze row
versions",
> +                             ShareUpdateExclusiveLock,
> +          0, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age),
> +                             -1, 0, 2000000000);
> +    optionsSpecSetAddInt(stdrd_relopt_spec_set,"log_autovacuum_min_duration",
> +                             "Sets the minimum execution time above which autovacuum actions will be logged",
> +                             ShareUpdateExclusiveLock,
> +                    0, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration),
> +                             -1, -1, INT_MAX);
> +    optionsSpecSetAddReal(stdrd_relopt_spec_set, "autovacuum_vacuum_cost_delay",
> +                         "Vacuum cost delay in milliseconds, for autovacuum",
> +                             ShareUpdateExclusiveLock,
> +                   0, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay),
> +                             -1, 0.0, 100.0);
> +    optionsSpecSetAddReal(stdrd_relopt_spec_set, "autovacuum_vacuum_scale_factor",
> +                              "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
> +                              ShareUpdateExclusiveLock,
> +                 0, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor),
> +                              -1, 0.0, 100.0);
> +
> +    optionsSpecSetAddReal(stdrd_relopt_spec_set, "autovacuum_vacuum_insert_scale_factor",
> +                              "Number of tuple inserts prior to vacuum as a fraction of reltuples",
> +                              ShareUpdateExclusiveLock,
> +                 0, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_scale_factor),
> +                              -1, 0.0, 100.0);
> +
> +    optionsSpecSetAddReal(stdrd_relopt_spec_set, "autovacuum_analyze_scale_factor",
> +                              "Number of tuple inserts, updates or deletes prior to analyze as a fraction of
reltuples",
> +                              ShareUpdateExclusiveLock,
> +                              is_for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0,
> +                   offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor),
> +                              -1, 0.0, 100.0);
> +
> +
> +
> +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "toast_tuple_target",
> +                                 "Sets the target tuple length at which external columns will be toasted",
> +                                ShareUpdateExclusiveLock,
> +                                is_for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0,
> +                                offsetof(StdRdOptions, toast_tuple_target),
> +                          TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN);
> +
> +    optionsSpecSetAddBool(stdrd_relopt_spec_set, "user_catalog_table",
> +                                  "Declare a table as an additional catalog table, e.g. for the purpose of logical
replication",
> +                                  AccessExclusiveLock,
> +                                is_for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0,
> +                                 offsetof(StdRdOptions, user_catalog_table),
> +                                  false);
> +
> +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "parallel_workers",
> +                                "Number of parallel processes that can be used per executor node for this
relation.",
> +                                ShareUpdateExclusiveLock,
> +                                is_for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0,
> +                                offsetof(StdRdOptions, parallel_workers),
> +                                -1, 0, 1024);
> +
> +    optionsSpecSetAddEnum(stdrd_relopt_spec_set, "vacuum_index_cleanup",
> +                                "Controls index vacuuming and index cleanup",
> +                                ShareUpdateExclusiveLock, 0,
> +                                offsetof(StdRdOptions, vacuum_index_cleanup),
> +                                StdRdOptIndexCleanupValues,
> +                                STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO,
> +                                gettext_noop("Valid values are \"on\", \"off\", and \"auto\"."));
> +
> +    optionsSpecSetAddBool(stdrd_relopt_spec_set, "vacuum_truncate",
> +                                "Enables vacuum to truncate empty pages at the end of this table",
> +                                ShareUpdateExclusiveLock, 0,
> +                                offsetof(StdRdOptions, vacuum_truncate),
> +                                true);
> +
> +// FIXME Do something with OIDS
> +
> +    return stdrd_relopt_spec_set;
> +}
> +
> +
> +static options_spec_set *heap_relopt_spec_set = NULL;
> +
> +options_spec_set *
> +get_heap_relopt_spec_set(void)
> +{
> +    if (heap_relopt_spec_set)
> +        return heap_relopt_spec_set;
> +    heap_relopt_spec_set = get_stdrd_relopt_spec_set(RELOPT_KIND_HEAP);
> +    return heap_relopt_spec_set;
> +}
> +
> +static options_spec_set *toast_relopt_spec_set = NULL;
> +
> +options_spec_set *
> +get_toast_relopt_spec_set(void)
> +{
> +    if (toast_relopt_spec_set)
> +        return toast_relopt_spec_set;
> +    toast_relopt_spec_set = get_stdrd_relopt_spec_set(RELOPT_KIND_TOAST);
> +    return toast_relopt_spec_set;
> +}
> +
> +static options_spec_set *partitioned_relopt_spec_set = NULL;
>  
> -    for (i = 0; i < noptions; i++)
> -    {
> -        char       *text_str = VARDATA(optiondatums[i]);
> -        int            text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
> -        int            j;
> -
> -        /* Search for a match in reloptions */
> -        for (j = 0; j < numoptions; j++)
> -        {
> -            int            kw_len = reloptions[j].gen->namelen;
> -
> -            if (text_len > kw_len && text_str[kw_len] == '=' &&
> -                strncmp(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);
> +options_spec_set *
> +get_partitioned_relopt_spec_set(void)
> +{
> +    if (partitioned_relopt_spec_set)
> +        return partitioned_relopt_spec_set;
> +    partitioned_relopt_spec_set = allocateOptionsSpecSet(
> +                    NULL,  sizeof(StdRdOptions), 0);
> +    /* No options for now, so spec set is empty */
>  
> -    if (((void *) array) != DatumGetPointer(options))
> -        pfree(array);
> +    return partitioned_relopt_spec_set;
>  }
>  
>  /*
> - * Interpret reloptions that are given in text-array format.
> - *
> - * options is a reloption text array as constructed by transformRelOptions.
> - * kind specifies the family of options to be processed.
> - *
> - * The return value is a relopt_value * array on which the options actually
> - * set in the options array are marked with isset=true.  The length of this
> - * array is returned in *numrelopts.  Options not set are also present in the
> - * array; this is so that the caller can easily locate the default values.
> - *
> - * If there are no options of the given kind, numrelopts is set to 0 and NULL
> - * is returned (unless options are illegally supplied despite none being
> - * defined, in which case an error occurs).
> - *
> - * Note: values of type int, bool and real are allocated as part of the
> - * returned array.  Values of type string are allocated separately and must
> - * be freed by the caller.
> + * Parse local options, allocate a bytea struct that's of the specified
> + * 'base_size' plus any extra space that's needed for string variables,
> + * fill its option's fields located at the given offsets and return it.
>   */
> -static relopt_value *
> -parseRelOptions(Datum options, bool validate, relopt_kind kind,
> -                int *numrelopts)
> -{
> -    relopt_value *reloptions = NULL;
> -    int            numoptions = 0;
> -    int            i;
> -    int            j;
> -
> -    if (need_initialization)
> -        initialize_reloptions();
> -
> -    /* Build a list of expected options, based on kind */
> -
> -    for (i = 0; relOpts[i]; i++)
> -        if (relOpts[i]->kinds & kind)
> -            numoptions++;
> -
> -    if (numoptions > 0)
> -    {
> -        reloptions = palloc(numoptions * sizeof(relopt_value));
> -
> -        for (i = 0, j = 0; relOpts[i]; i++)
> -        {
> -            if (relOpts[i]->kinds & kind)
> -            {
> -                reloptions[j].gen = relOpts[i];
> -                reloptions[j].isset = false;
> -                j++;
> -            }
> -        }
> -    }
> -
> -    /* Done if no options */
> -    if (PointerIsValid(DatumGetPointer(options)))
> -        parseRelOptionsInternal(options, validate, reloptions, numoptions);
> -
> -    *numrelopts = numoptions;
> -    return reloptions;
> -}
> -
> -/* Parse local unregistered options. */
> -static relopt_value *
> -parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
> +void *
> +build_local_reloptions(local_relopts *relopts, Datum options, bool validate)
>  {
> -    int            nopts = list_length(relopts->options);
> -    relopt_value *values = palloc(sizeof(*values) * nopts);
> +    void       *opts;
>      ListCell   *lc;
> -    int            i = 0;
> -
> -    foreach(lc, relopts->options)
> -    {
> -        local_relopt *opt = lfirst(lc);
> -
> -        values[i].gen = opt->option;
> -        values[i].isset = false;
> -
> -        i++;
> -    }
> -
> -    if (options != (Datum) 0)
> -        parseRelOptionsInternal(options, validate, values, nopts);
> +    opts = (void *) optionsTextArrayToBytea(relopts->spec_set, options, validate);
>  
> -    return values;
> -}
> -
> -/*
> - * Subroutine for parseRelOptions, to parse and validate a single option's
> - * value
> - */
> -static void
> -parse_one_reloption(relopt_value *option, char *text_str, int text_len,
> -                    bool validate)
> -{
> -    char       *value;
> -    int            value_len;
> -    bool        parsed;
> -    bool        nofree = false;
> -
> -    if (option->isset && validate)
> -        ereport(ERROR,
> -                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> -                 errmsg("parameter \"%s\" specified more than once",
> -                        option->gen->name)));
> -
> -    value_len = text_len - option->gen->namelen - 1;
> -    value = (char *) palloc(value_len + 1);
> -    memcpy(value, text_str + option->gen->namelen + 1, value_len);
> -    value[value_len] = '\0';
> -
> -    switch (option->gen->type)
> -    {
> -        case RELOPT_TYPE_BOOL:
> -            {
> -                parsed = parse_bool(value, &option->values.bool_val);
> -                if (validate && !parsed)
> -                    ereport(ERROR,
> -                            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> -                             errmsg("invalid value for boolean option \"%s\": %s",
> -                                    option->gen->name, value)));
> -            }
> -            break;
> -        case RELOPT_TYPE_INT:
> -            {
> -                relopt_int *optint = (relopt_int *) option->gen;
> -
> -                parsed = parse_int(value, &option->values.int_val, 0, NULL);
> -                if (validate && !parsed)
> -                    ereport(ERROR,
> -                            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> -                             errmsg("invalid value for integer option \"%s\": %s",
> -                                    option->gen->name, value)));
> -                if (validate && (option->values.int_val < optint->min ||
> -                                 option->values.int_val > optint->max))
> -                    ereport(ERROR,
> -                            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> -                             errmsg("value %s out of bounds for option \"%s\"",
> -                                    value, option->gen->name),
> -                             errdetail("Valid values are between \"%d\" and \"%d\".",
> -                                       optint->min, optint->max)));
> -            }
> -            break;
> -        case RELOPT_TYPE_REAL:
> -            {
> -                relopt_real *optreal = (relopt_real *) option->gen;
> -
> -                parsed = parse_real(value, &option->values.real_val, 0, NULL);
> -                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_ENUM:
> -            {
> -                relopt_enum *optenum = (relopt_enum *) option->gen;
> -                relopt_enum_elt_def *elt;
> -
> -                parsed = false;
> -                for (elt = optenum->members; elt->string_val; elt++)
> -                {
> -                    if (pg_strcasecmp(value, elt->string_val) == 0)
> -                    {
> -                        option->values.enum_val = elt->symbol_val;
> -                        parsed = true;
> -                        break;
> -                    }
> -                }
> -                if (validate && !parsed)
> -                    ereport(ERROR,
> -                            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> -                             errmsg("invalid value for enum option \"%s\": %s",
> -                                    option->gen->name, value),
> -                             optenum->detailmsg ?
> -                             errdetail_internal("%s", _(optenum->detailmsg)) : 0));
> -
> -                /*
> -                 * If value is not among the allowed string values, but we are
> -                 * not asked to validate, just use the default numeric value.
> -                 */
> -                if (!parsed)
> -                    option->values.enum_val = optenum->default_val;
> -            }
> -            break;
> -        case RELOPT_TYPE_STRING:
> -            {
> -                relopt_string *optstring = (relopt_string *) option->gen;
> -
> -                option->values.string_val = value;
> -                nofree = true;
> -                if (validate && optstring->validate_cb)
> -                    (optstring->validate_cb) (value);
> -                parsed = true;
> -            }
> -            break;
> -        default:
> -            elog(ERROR, "unsupported reloption type %d", option->gen->type);
> -            parsed = true;        /* quiet compiler */
> -            break;
> -    }
> +    foreach(lc, relopts->validators)
> +        ((relopts_validator) lfirst(lc)) (opts, NULL, 0);
> +//        ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
> +// FIXME solve problem with validation of separate option values;
> +    return opts;
>  
> -    if (parsed)
> -        option->isset = true;
> -    if (!nofree)
> -        pfree(value);
>  }
>  
>  /*
> - * Given the result from parseRelOptions, allocate a struct that's of the
> - * specified base size plus any extra space that's needed for string variables.
> - *
> - * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
> - * equivalent).
> + * get_view_relopt_spec_set
> + *        Returns an options catalog for view relation.
>   */
> -static void *
> -allocateReloptStruct(Size base, relopt_value *options, int numoptions)
> -{
> -    Size        size = base;
> -    int            i;
> -
> -    for (i = 0; i < numoptions; i++)
> -    {
> -        relopt_value *optval = &options[i];
> -
> -        if (optval->gen->type == RELOPT_TYPE_STRING)
> -        {
> -            relopt_string *optstr = (relopt_string *) optval->gen;
> -
> -            if (optstr->fill_cb)
> -            {
> -                const char *val = optval->isset ? optval->values.string_val :
> -                optstr->default_isnull ? NULL : optstr->default_val;
> -
> -                size += optstr->fill_cb(val, NULL);
> -            }
> -            else
> -                size += GET_STRING_RELOPTION_LEN(*optval) + 1;
> -        }
> -    }
> -
> -    return palloc0(size);
> -}
> +static options_spec_set *view_relopt_spec_set = NULL;
>  
> -/*
> - * 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.
> - */
> -static void
> -fillRelOptions(void *rdopts, Size basesize,
> -               relopt_value *options, int numoptions,
> -               bool validate,
> -               const relopt_parse_elt *elems, int numelems)
> +options_spec_set *
> +get_view_relopt_spec_set(void)
>  {
> -    int            i;
> -    int            offset = basesize;
> +    if (view_relopt_spec_set)
> +        return view_relopt_spec_set;
>  
> -    for (i = 0; i < numoptions; i++)
> -    {
> -        int            j;
> -        bool        found = false;
> +    view_relopt_spec_set = allocateOptionsSpecSet(NULL,
> +                                                 sizeof(ViewOptions), 2);
>  
> -        for (j = 0; j < numelems; j++)
> -        {
> -            if (strcmp(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_ENUM:
> -                        *(int *) itempos = options[i].isset ?
> -                            options[i].values.enum_val :
> -                            ((relopt_enum *) options[i].gen)->default_val;
> -                        break;
> -                    case RELOPT_TYPE_STRING:
> -                        optstring = (relopt_string *) options[i].gen;
> -                        if (options[i].isset)
> -                            string_val = options[i].values.string_val;
> -                        else if (!optstring->default_isnull)
> -                            string_val = optstring->default_val;
> -                        else
> -                            string_val = NULL;
> -
> -                        if (optstring->fill_cb)
> -                        {
> -                            Size        size =
> -                            optstring->fill_cb(string_val,
> -                                               (char *) rdopts + offset);
> -
> -                            if (size)
> -                            {
> -                                *(int *) itempos = offset;
> -                                offset += size;
> -                            }
> -                            else
> -                                *(int *) itempos = 0;
> -                        }
> -                        else if (string_val == NULL)
> -                            *(int *) itempos = 0;
> -                        else
> -                        {
> -                            strcpy((char *) rdopts + offset, string_val);
> -                            *(int *) itempos = offset;
> -                            offset += strlen(string_val) + 1;
> -                        }
> -                        break;
> -                    default:
> -                        elog(ERROR, "unsupported reloption type %d",
> -                             options[i].gen->type);
> -                        break;
> -                }
> -                found = true;
> -                break;
> -            }
> -        }
> -        if (validate && !found)
> -            elog(ERROR, "reloption \"%s\" not found in parse table",
> -                 options[i].gen->name);
> -    }
> -    SET_VARSIZE(rdopts, offset);
> -}
> +    optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
> +                              "View acts as a row security barrier",
> +                              AccessExclusiveLock,
> +                      0, offsetof(ViewOptions, security_barrier), false);
>  
> +    optionsSpecSetAddEnum(view_relopt_spec_set, "check_option",
> +                           "View has WITH CHECK OPTION defined (local or cascaded)",
> +                              AccessExclusiveLock, 0,
> +                              offsetof(ViewOptions, check_option),
> +                              viewCheckOptValues,
> +                              VIEW_OPTION_CHECK_OPTION_NOT_SET,
> +                              gettext_noop("Valid values are \"local\" and \"cascaded\"."));
>  
> -/*
> - * Option parser for anything that uses StdRdOptions.
> - */
> -bytea *
> -default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
> -{
> -    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_vacuum_insert_threshold", RELOPT_TYPE_INT,
> -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_threshold)},
> -        {"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
> -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
> -        {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
> -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
> -        {"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
> -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
> -        {"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
> -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
> -        {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
> -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
> -        {"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
> -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age)},
> -        {"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
> -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)},
> -        {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
> -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
> -        {"log_autovacuum_min_duration", RELOPT_TYPE_INT,
> -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)},
> -        {"toast_tuple_target", RELOPT_TYPE_INT,
> -        offsetof(StdRdOptions, toast_tuple_target)},
> -        {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_REAL,
> -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)},
> -        {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
> -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
> -        {"autovacuum_vacuum_insert_scale_factor", RELOPT_TYPE_REAL,
> -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_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)},
> -        {"vacuum_index_cleanup", RELOPT_TYPE_ENUM,
> -        offsetof(StdRdOptions, vacuum_index_cleanup)},
> -        {"vacuum_truncate", RELOPT_TYPE_BOOL,
> -        offsetof(StdRdOptions, vacuum_truncate)}
> -    };
> -
> -    return (bytea *) build_reloptions(reloptions, validate, kind,
> -                                      sizeof(StdRdOptions),
> -                                      tab, lengthof(tab));
> +    return view_relopt_spec_set;
>  }
>  
>  /*
> - * build_reloptions
> - *
> - * Parses "reloptions" provided by the caller, returning them in a
> - * structure containing the parsed options.  The parsing is done with
> - * the help of a parsing table describing the allowed options, defined
> - * by "relopt_elems" of length "num_relopt_elems".
> - *
> - * "validate" must be true if reloptions value is freshly built by
> - * transformRelOptions(), as opposed to being read from the catalog, in which
> - * case the values contained in it must already be valid.
> - *
> - * NULL is returned if the passed-in options did not match any of the options
> - * in the parsing table, unless validate is true in which case an error would
> - * be reported.
> + * get_attribute_options_spec_set
> + *        Returns an options spec det for heap attributes
>   */
> -void *
> -build_reloptions(Datum reloptions, bool validate,
> -                 relopt_kind kind,
> -                 Size relopt_struct_size,
> -                 const relopt_parse_elt *relopt_elems,
> -                 int num_relopt_elems)
> -{
> -    int            numoptions;
> -    relopt_value *options;
> -    void       *rdopts;
> -
> -    /* parse options specific to given relation option kind */
> -    options = parseRelOptions(reloptions, validate, kind, &numoptions);
> -    Assert(numoptions <= num_relopt_elems);
> -
> -    /* if none set, we're done */
> -    if (numoptions == 0)
> -    {
> -        Assert(options == NULL);
> -        return NULL;
> -    }
> -
> -    /* allocate and fill the structure */
> -    rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
> -    fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
> -                   validate, relopt_elems, num_relopt_elems);
> +static options_spec_set *attribute_options_spec_set = NULL;
>  
> -    pfree(options);
> -
> -    return rdopts;
> -}
> -
> -/*
> - * Parse local options, allocate a bytea struct that's of the specified
> - * 'base_size' plus any extra space that's needed for string variables,
> - * fill its option's fields located at the given offsets and return it.
> - */
> -void *
> -build_local_reloptions(local_relopts *relopts, Datum options, bool validate)
> +options_spec_set *
> +get_attribute_options_spec_set(void)
>  {
> -    int            noptions = list_length(relopts->options);
> -    relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
> -    relopt_value *vals;
> -    void       *opts;
> -    int            i = 0;
> -    ListCell   *lc;
> +    if (attribute_options_spec_set)
> +            return attribute_options_spec_set;
>  
> -    foreach(lc, relopts->options)
> -    {
> -        local_relopt *opt = lfirst(lc);
> -
> -        elems[i].optname = opt->option->name;
> -        elems[i].opttype = opt->option->type;
> -        elems[i].offset = opt->offset;
> -
> -        i++;
> -    }
> +    attribute_options_spec_set = allocateOptionsSpecSet(NULL,
> +                                               sizeof(AttributeOpts), 2);
>  
> -    vals = parseLocalRelOptions(relopts, options, validate);
> -    opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
> -    fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
> -                   elems, noptions);
> +    optionsSpecSetAddReal(attribute_options_spec_set, "n_distinct",
> +                          "Sets the planner's estimate of the number of distinct values appearing in a column
(excludingchild relations).",
 
> +                          ShareUpdateExclusiveLock,
> +               0, offsetof(AttributeOpts, n_distinct), 0, -1.0, DBL_MAX);
>  
> -    foreach(lc, relopts->validators)
> -        ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
> -
> -    if (elems)
> -        pfree(elems);
> +    optionsSpecSetAddReal(attribute_options_spec_set,
> +                          "n_distinct_inherited",
> +                          "Sets the planner's estimate of the number of distinct values appearing in a column
(includingchild relations).",
 
> +                          ShareUpdateExclusiveLock,
> +     0, offsetof(AttributeOpts, n_distinct_inherited), 0, -1.0, DBL_MAX);
>  
> -    return opts;
> +    return attribute_options_spec_set;
>  }
>  
> -/*
> - * Option parser for partitioned tables
> - */
> -bytea *
> -partitioned_table_reloptions(Datum reloptions, bool validate)
> -{
> -    /*
> -     * There are no options for partitioned tables yet, but this is able to do
> -     * some validation.
> -     */
> -    return (bytea *) build_reloptions(reloptions, validate,
> -                                      RELOPT_KIND_PARTITIONED,
> -                                      0, NULL, 0);
> -}
>  
>  /*
> - * Option parser for views
> - */
> -bytea *
> -view_reloptions(Datum reloptions, bool validate)
> -{
> -    static const relopt_parse_elt tab[] = {
> -        {"security_barrier", RELOPT_TYPE_BOOL,
> -        offsetof(ViewOptions, security_barrier)},
> -        {"check_option", RELOPT_TYPE_ENUM,
> -        offsetof(ViewOptions, check_option)}
> -    };
> -
> -    return (bytea *) build_reloptions(reloptions, validate,
> -                                      RELOPT_KIND_VIEW,
> -                                      sizeof(ViewOptions),
> -                                      tab, lengthof(tab));
> -}
> + * get_tablespace_options_spec_set
> + *        Returns an options spec set for tablespaces
> +*/
> +static options_spec_set *tablespace_options_spec_set = NULL;
>  
> -/*
> - * Parse options for heaps, views and toast tables.
> - */
> -bytea *
> -heap_reloptions(char relkind, Datum reloptions, bool validate)
> +options_spec_set *
> +get_tablespace_options_spec_set(void)
>  {
> -    StdRdOptions *rdopts;
> -
> -    switch (relkind)
> +    if (!tablespace_options_spec_set)
>      {
> -        case RELKIND_TOASTVALUE:
> -            rdopts = (StdRdOptions *)
> -                default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
> -            if (rdopts != NULL)
> -            {
> -                /* adjust default-only parameters for TOAST relations */
> -                rdopts->fillfactor = 100;
> -                rdopts->autovacuum.analyze_threshold = -1;
> -                rdopts->autovacuum.analyze_scale_factor = -1;
> -            }
> -            return (bytea *) rdopts;
> -        case RELKIND_RELATION:
> -        case RELKIND_MATVIEW:
> -            return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
> -        default:
> -            /* other relkinds are not supported */
> -            return NULL;
> -    }
> -}
> -
> -
> -/*
> - * Parse options for indexes.
> - *
> - *    amoptions    index AM's option parser function
> - *    reloptions    options as text[] datum
> - *    validate    error flag
> - */
> -bytea *
> -index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
> -{
> -    Assert(amoptions != NULL);
> +        tablespace_options_spec_set = allocateOptionsSpecSet(NULL,
> +                                                  sizeof(TableSpaceOpts), 4);
>  
> -    /* Assume function is strict */
> -    if (!PointerIsValid(DatumGetPointer(reloptions)))
> -        return NULL;
> +        optionsSpecSetAddReal(tablespace_options_spec_set,
> +                                  "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);
>  
> -    return amoptions(reloptions, validate);
> -}
> +        optionsSpecSetAddReal(tablespace_options_spec_set, "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);
>  
> -/*
> - * Option parser for attribute reloptions
> - */
> -bytea *
> -attribute_reloptions(Datum reloptions, bool validate)
> -{
> -    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)}
> -    };
> -
> -    return (bytea *) build_reloptions(reloptions, validate,
> -                                      RELOPT_KIND_ATTRIBUTE,
> -                                      sizeof(AttributeOpts),
> -                                      tab, lengthof(tab));
> -}
> +        optionsSpecSetAddInt(tablespace_options_spec_set,
> +                                 "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
> +            );
>  
> -/*
> - * Option parser for tablespace reloptions
> - */
> -bytea *
> -tablespace_reloptions(Datum reloptions, bool validate)
> -{
> -    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)},
> -        {"maintenance_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, maintenance_io_concurrency)}
> -    };
> -
> -    return (bytea *) build_reloptions(reloptions, validate,
> -                                      RELOPT_KIND_TABLESPACE,
> -                                      sizeof(TableSpaceOpts),
> -                                      tab, lengthof(tab));
> +        optionsSpecSetAddInt(tablespace_options_spec_set,
> +                                 "maintenance_io_concurrency",
> +                                 "Number of simultaneous requests that can be handled efficiently by the disk
subsystemfor maintenance work.",
 
> +                                 ShareUpdateExclusiveLock,
> +                       0, offsetof(TableSpaceOpts, maintenance_io_concurrency),
> +#ifdef USE_PREFETCH
> +                                 -1, 0, MAX_IO_CONCURRENCY
> +#else
> +                                 0, 0, 0
> +#endif
> +            );
> +    }
> +    return tablespace_options_spec_set;
>  }
>  
>  /*
> @@ -2099,33 +612,55 @@ tablespace_reloptions(Datum reloptions, bool validate)
>   * for a longer explanation of how this works.
>   */
>  LOCKMODE
> -AlterTableGetRelOptionsLockLevel(List *defList)
> +AlterTableGetRelOptionsLockLevel(Relation rel, List *defList)
>  {
>      LOCKMODE    lockmode = NoLock;
>      ListCell   *cell;
> +    options_spec_set *spec_set = NULL;
>  
>      if (defList == NIL)
>          return AccessExclusiveLock;
>  
> -    if (need_initialization)
> -        initialize_reloptions();
> +    switch (rel->rd_rel->relkind)
> +    {
> +        case RELKIND_TOASTVALUE:
> +            spec_set = get_toast_relopt_spec_set();
> +            break;
> +        case RELKIND_RELATION:
> +        case RELKIND_MATVIEW:
> +            spec_set = get_heap_relopt_spec_set();
> +            break;
> +        case RELKIND_INDEX:
> +            spec_set = rel->rd_indam->amreloptspecset();
> +            break;
> +        case RELKIND_VIEW:
> +            spec_set = get_view_relopt_spec_set();
> +            break;
> +        case RELKIND_PARTITIONED_TABLE:
> +            spec_set = get_partitioned_relopt_spec_set();
> +            break;
> +        default:
> +            Assert(false);        /* can't get here */
> +            break;
> +    }
> +    Assert(spec_set);            /* No spec set - no reloption change. Should
> +                                 * never get here */
>  
>      foreach(cell, defList)
>      {
>          DefElem    *def = (DefElem *) lfirst(cell);
> +
>          int            i;
>  
> -        for (i = 0; relOpts[i]; i++)
> +        for (i = 0; i < spec_set->num; i++)
>          {
> -            if (strncmp(relOpts[i]->name,
> -                        def->defname,
> -                        relOpts[i]->namelen + 1) == 0)
> -            {
> -                if (lockmode < relOpts[i]->lockmode)
> -                    lockmode = relOpts[i]->lockmode;
> -            }
> +            option_spec_basic *gen = spec_set->definitions[i];
> +
> +            if (pg_strcasecmp(gen->name,
> +                              def->defname) == 0)
> +                if (lockmode < gen->lockmode)
> +                    lockmode = gen->lockmode;
>          }
>      }
> -
>      return lockmode;
> -}
> +}
> \ No newline at end of file
> diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c
> index 0e8672c..0cbffad 100644
> --- a/src/backend/access/gin/gininsert.c
> +++ b/src/backend/access/gin/gininsert.c
> @@ -512,6 +512,8 @@ gininsert(Relation index, Datum *values, bool *isnull,
>  
>      oldCtx = MemoryContextSwitchTo(insertCtx);
>  
> +// elog(WARNING, "GinGetUseFastUpdate = %i", GinGetUseFastUpdate(index));
> +
>      if (GinGetUseFastUpdate(index))
>      {
>          GinTupleCollector collector;
> diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
> index 6d2d71b..d1fa3a0 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"
> @@ -28,6 +28,7 @@
>  #include "utils/builtins.h"
>  #include "utils/index_selfuncs.h"
>  #include "utils/typcache.h"
> +#include "utils/guc.h"
>  
>  
>  /*
> @@ -67,7 +68,6 @@ ginhandler(PG_FUNCTION_ARGS)
>      amroutine->amvacuumcleanup = ginvacuumcleanup;
>      amroutine->amcanreturn = NULL;
>      amroutine->amcostestimate = gincostestimate;
> -    amroutine->amoptions = ginoptions;
>      amroutine->amproperty = NULL;
>      amroutine->ambuildphasename = NULL;
>      amroutine->amvalidate = ginvalidate;
> @@ -82,6 +82,7 @@ ginhandler(PG_FUNCTION_ARGS)
>      amroutine->amestimateparallelscan = NULL;
>      amroutine->aminitparallelscan = NULL;
>      amroutine->amparallelrescan = NULL;
> +    amroutine->amreloptspecset = gingetreloptspecset;
>  
>      PG_RETURN_POINTER(amroutine);
>  }
> @@ -604,6 +605,7 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
>      return entries;
>  }
>  
> +/*
>  bytea *
>  ginoptions(Datum reloptions, bool validate)
>  {
> @@ -618,6 +620,7 @@ ginoptions(Datum reloptions, bool validate)
>                                        sizeof(GinOptions),
>                                        tab, lengthof(tab));
>  }
> +*/
>  
>  /*
>   * Fetch index's statistical data into *stats
> @@ -705,3 +708,31 @@ ginUpdateStats(Relation index, const GinStatsData *stats, bool is_build)
>  
>      END_CRIT_SECTION();
>  }
> +
> +static options_spec_set *gin_relopt_specset = NULL;
> +
> +void *
> +gingetreloptspecset(void)
> +{
> +    if (gin_relopt_specset)
> +        return gin_relopt_specset;
> +
> +    gin_relopt_specset = allocateOptionsSpecSet(NULL,
> +                                                sizeof(GinOptions), 2);
> +
> +    optionsSpecSetAddBool(gin_relopt_specset, "fastupdate",
> +                        "Enables \"fast update\" feature for this GIN index",
> +                              AccessExclusiveLock,
> +                              0,
> +                              offsetof(GinOptions, useFastUpdate),
> +                              GIN_DEFAULT_USE_FASTUPDATE);
> +
> +    optionsSpecSetAddInt(gin_relopt_specset, "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_specset;
> +}
> diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
> index 0683f42..cbbc6a5 100644
> --- a/src/backend/access/gist/gist.c
> +++ b/src/backend/access/gist/gist.c
> @@ -88,7 +88,6 @@ gisthandler(PG_FUNCTION_ARGS)
>      amroutine->amvacuumcleanup = gistvacuumcleanup;
>      amroutine->amcanreturn = gistcanreturn;
>      amroutine->amcostestimate = gistcostestimate;
> -    amroutine->amoptions = gistoptions;
>      amroutine->amproperty = gistproperty;
>      amroutine->ambuildphasename = NULL;
>      amroutine->amvalidate = gistvalidate;
> @@ -103,6 +102,7 @@ gisthandler(PG_FUNCTION_ARGS)
>      amroutine->amestimateparallelscan = NULL;
>      amroutine->aminitparallelscan = NULL;
>      amroutine->amparallelrescan = NULL;
> +    amroutine->amreloptspecset = gistgetreloptspecset;
>  
>      PG_RETURN_POINTER(amroutine);
>  }
> diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
> index baad28c..931d249 100644
> --- a/src/backend/access/gist/gistbuild.c
> +++ b/src/backend/access/gist/gistbuild.c
> @@ -215,6 +215,7 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
>              buildstate.buildMode = GIST_BUFFERING_DISABLED;
>          else                    /* must be "auto" */
>              buildstate.buildMode = GIST_BUFFERING_AUTO;
> +//elog(WARNING, "biffering_mode = %i", options->buffering_mode);
>      }
>      else
>      {
> diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
> index 43ba03b..0391915 100644
> --- a/src/backend/access/gist/gistutil.c
> +++ b/src/backend/access/gist/gistutil.c
> @@ -17,7 +17,7 @@
>  
>  #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"
> @@ -916,20 +916,6 @@ gistPageRecyclable(Page page)
>      return false;
>  }
>  
> -bytea *
> -gistoptions(Datum reloptions, bool validate)
> -{
> -    static const relopt_parse_elt tab[] = {
> -        {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
> -        {"buffering", RELOPT_TYPE_ENUM, offsetof(GiSTOptions, buffering_mode)}
> -    };
> -
> -    return (bytea *) build_reloptions(reloptions, validate,
> -                                      RELOPT_KIND_GIST,
> -                                      sizeof(GiSTOptions),
> -                                      tab, lengthof(tab));
> -}
> -
>  /*
>   *    gistproperty() -- Check boolean properties of indexes.
>   *
> @@ -1064,3 +1050,42 @@ gistGetFakeLSN(Relation rel)
>          return GetFakeLSNForUnloggedRel();
>      }
>  }
> +
> +/* values from GistOptBufferingMode */
> +opt_enum_elt_def gistBufferingOptValues[] =
> +{
> +    {"auto", GIST_OPTION_BUFFERING_AUTO},
> +    {"on", GIST_OPTION_BUFFERING_ON},
> +    {"off", GIST_OPTION_BUFFERING_OFF},
> +    {(const char *) NULL}        /* list terminator */
> +};
> +
> +static options_spec_set *gist_relopt_specset = NULL;
> +
> +void *
> +gistgetreloptspecset(void)
> +{
> +    if (gist_relopt_specset)
> +        return gist_relopt_specset;
> +
> +    gist_relopt_specset = allocateOptionsSpecSet(NULL,
> +                                                 sizeof(GiSTOptions), 2);
> +
> +    optionsSpecSetAddInt(gist_relopt_specset, "fillfactor",
> +                        "Packs gist index pages only to this percentage",
> +                             NoLock,        /* No ALTER, no lock */
> +                             0,
> +                             offsetof(GiSTOptions, fillfactor),
> +                             GIST_DEFAULT_FILLFACTOR,
> +                             GIST_MIN_FILLFACTOR, 100);
> +
> +    optionsSpecSetAddEnum(gist_relopt_specset, "buffering",
> +                           "Enables buffering build for this GiST index",
> +                              NoLock,        /* No ALTER, no lock */
> +                              0,
> +                              offsetof(GiSTOptions, buffering_mode),
> +                              gistBufferingOptValues,
> +                              GIST_OPTION_BUFFERING_AUTO,
> +                              gettext_noop("Valid values are \"on\", \"off\", and \"auto\"."));
> +    return gist_relopt_specset;
> +}
> diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
> index eb38104..8dc4ca7 100644
> --- a/src/backend/access/hash/hash.c
> +++ b/src/backend/access/hash/hash.c
> @@ -85,7 +85,6 @@ hashhandler(PG_FUNCTION_ARGS)
>      amroutine->amvacuumcleanup = hashvacuumcleanup;
>      amroutine->amcanreturn = NULL;
>      amroutine->amcostestimate = hashcostestimate;
> -    amroutine->amoptions = hashoptions;
>      amroutine->amproperty = NULL;
>      amroutine->ambuildphasename = NULL;
>      amroutine->amvalidate = hashvalidate;
> @@ -100,6 +99,7 @@ hashhandler(PG_FUNCTION_ARGS)
>      amroutine->amestimateparallelscan = NULL;
>      amroutine->aminitparallelscan = NULL;
>      amroutine->amparallelrescan = NULL;
> +    amroutine->amreloptspecset = hashgetreloptspecset;
>  
>      PG_RETURN_POINTER(amroutine);
>  }
> diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
> index 159646c..38f64ef 100644
> --- a/src/backend/access/hash/hashpage.c
> +++ b/src/backend/access/hash/hashpage.c
> @@ -359,6 +359,8 @@ _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 */
> +//elog(WARNING, "fillfactor = %i", HashGetFillFactor(rel));
> +
>      ffactor = HashGetTargetPageUsage(rel) / item_width;
>      /* keep to a sane range */
>      if (ffactor < 10)
> diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
> index 5198728..826beab 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 "port/pg_bitutils.h"
>  #include "storage/buf_internals.h"
> @@ -272,19 +272,6 @@ _hash_checkpage(Relation rel, Buffer buf, int flags)
>      }
>  }
>  
> -bytea *
> -hashoptions(Datum reloptions, bool validate)
> -{
> -    static const relopt_parse_elt tab[] = {
> -        {"fillfactor", RELOPT_TYPE_INT, offsetof(HashOptions, fillfactor)},
> -    };
> -
> -    return (bytea *) build_reloptions(reloptions, validate,
> -                                      RELOPT_KIND_HASH,
> -                                      sizeof(HashOptions),
> -                                      tab, lengthof(tab));
> -}
> -
>  /*
>   * _hash_get_indextuple_hashkey - get the hash index tuple's hash key value
>   */
> @@ -620,3 +607,24 @@ _hash_kill_items(IndexScanDesc scan)
>      else
>          _hash_relbuf(rel, buf);
>  }
> +
> +static options_spec_set *hash_relopt_specset = NULL;
> +
> +void *
> +hashgetreloptspecset(void)
> +{
> +    if (hash_relopt_specset)
> +        return hash_relopt_specset;
> +
> +    hash_relopt_specset = allocateOptionsSpecSet(NULL,
> +                                              sizeof(HashOptions), 1);
> +    optionsSpecSetAddInt(hash_relopt_specset, "fillfactor",
> +                        "Packs hash index pages only to this percentage",
> +                             NoLock,        /* No ALTER -- no lock */
> +                             0,
> +                             offsetof(HashOptions, fillfactor),
> +                             HASH_DEFAULT_FILLFACTOR,
> +                             HASH_MIN_FILLFACTOR, 100);
> +
> +    return hash_relopt_specset;
> +}
> diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
> index 7355e1d..f7b117e 100644
> --- a/src/backend/access/nbtree/nbtinsert.c
> +++ b/src/backend/access/nbtree/nbtinsert.c
> @@ -2745,6 +2745,8 @@ _bt_delete_or_dedup_one_page(Relation rel, Relation heapRel,
>          _bt_bottomupdel_pass(rel, buffer, heapRel, insertstate->itemsz))
>          return;
>  
> +// elog(WARNING, "Deduplicate_items = %i", BTGetDeduplicateItems(rel));
> +
>      /* Perform deduplication pass (when enabled and index-is-allequalimage) */
>      if (BTGetDeduplicateItems(rel) && itup_key->allequalimage)
>          _bt_dedup_pass(rel, buffer, heapRel, insertstate->itup,
> diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
> index 40ad095..f171c54 100644
> --- a/src/backend/access/nbtree/nbtree.c
> +++ b/src/backend/access/nbtree/nbtree.c
> @@ -22,6 +22,7 @@
>  #include "access/nbtxlog.h"
>  #include "access/relscan.h"
>  #include "access/xlog.h"
> +#include "access/options.h"
>  #include "commands/progress.h"
>  #include "commands/vacuum.h"
>  #include "miscadmin.h"
> @@ -124,7 +125,6 @@ bthandler(PG_FUNCTION_ARGS)
>      amroutine->amvacuumcleanup = btvacuumcleanup;
>      amroutine->amcanreturn = btcanreturn;
>      amroutine->amcostestimate = btcostestimate;
> -    amroutine->amoptions = btoptions;
>      amroutine->amproperty = btproperty;
>      amroutine->ambuildphasename = btbuildphasename;
>      amroutine->amvalidate = btvalidate;
> @@ -139,6 +139,7 @@ bthandler(PG_FUNCTION_ARGS)
>      amroutine->amestimateparallelscan = btestimateparallelscan;
>      amroutine->aminitparallelscan = btinitparallelscan;
>      amroutine->amparallelrescan = btparallelrescan;
> +    amroutine->amreloptspecset = btgetreloptspecset;
>  
>      PG_RETURN_POINTER(amroutine);
>  }
> @@ -1418,3 +1419,37 @@ btcanreturn(Relation index, int attno)
>  {
>      return true;
>  }
> +
> +static options_spec_set *bt_relopt_specset = NULL;
> +
> +void *
> +btgetreloptspecset(void)
> +{
> +    if (bt_relopt_specset)
> +        return bt_relopt_specset;
> +
> +    bt_relopt_specset = allocateOptionsSpecSet(NULL,
> +                                               sizeof(BTOptions), 3);
> +
> +    optionsSpecSetAddInt(
> +        bt_relopt_specset, "fillfactor",
> +        "Packs btree index pages only to this percentage",
> +        ShareUpdateExclusiveLock, /* since it applies only to later inserts */
> +        0, offsetof(BTOptions, fillfactor),
> +        BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
> +    );
> +    optionsSpecSetAddReal(
> +        bt_relopt_specset, "vacuum_cleanup_index_scale_factor",
> +        "Number of tuple inserts prior to index cleanup as a fraction of reltuples",
> +        ShareUpdateExclusiveLock,
> +        0, offsetof(BTOptions,vacuum_cleanup_index_scale_factor),
> +        -1, 0.0, 1e10
> +    );
> +    optionsSpecSetAddBool(
> +        bt_relopt_specset, "deduplicate_items",
> +        "Enables \"deduplicate items\" feature for this btree index",
> +        ShareUpdateExclusiveLock, /* since it applies only to later inserts */
> +        0, offsetof(BTOptions,deduplicate_items), true
> +    );
> +    return bt_relopt_specset;
> +}
> diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
> index c72b456..2588a30 100644
> --- a/src/backend/access/nbtree/nbtutils.c
> +++ b/src/backend/access/nbtree/nbtutils.c
> @@ -18,7 +18,7 @@
>  #include <time.h>
>  
>  #include "access/nbtree.h"
> -#include "access/reloptions.h"
> +#include "storage/lock.h"
>  #include "access/relscan.h"
>  #include "catalog/catalog.h"
>  #include "commands/progress.h"
> @@ -2100,25 +2100,6 @@ BTreeShmemInit(void)
>          Assert(found);
>  }
>  
> -bytea *
> -btoptions(Datum reloptions, bool validate)
> -{
> -    static const relopt_parse_elt tab[] = {
> -        {"fillfactor", RELOPT_TYPE_INT, offsetof(BTOptions, fillfactor)},
> -        {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
> -        offsetof(BTOptions, vacuum_cleanup_index_scale_factor)},
> -        {"deduplicate_items", RELOPT_TYPE_BOOL,
> -        offsetof(BTOptions, deduplicate_items)}
> -
> -    };
> -
> -    return (bytea *) build_reloptions(reloptions, validate,
> -                                      RELOPT_KIND_BTREE,
> -                                      sizeof(BTOptions),
> -                                      tab, lengthof(tab));
> -
> -}
> -
>  /*
>   *    btproperty() -- Check boolean properties of indexes.
>   *
> diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
> index 03a9cd3..14429ad 100644
> --- a/src/backend/access/spgist/spgutils.c
> +++ b/src/backend/access/spgist/spgutils.c
> @@ -17,7 +17,7 @@
>  
>  #include "access/amvalidate.h"
>  #include "access/htup_details.h"
> -#include "access/reloptions.h"
> +#include "access/options.h"
>  #include "access/spgist_private.h"
>  #include "access/toast_compression.h"
>  #include "access/transam.h"
> @@ -72,7 +72,6 @@ spghandler(PG_FUNCTION_ARGS)
>      amroutine->amvacuumcleanup = spgvacuumcleanup;
>      amroutine->amcanreturn = spgcanreturn;
>      amroutine->amcostestimate = spgcostestimate;
> -    amroutine->amoptions = spgoptions;
>      amroutine->amproperty = spgproperty;
>      amroutine->ambuildphasename = NULL;
>      amroutine->amvalidate = spgvalidate;
> @@ -87,6 +86,7 @@ spghandler(PG_FUNCTION_ARGS)
>      amroutine->amestimateparallelscan = NULL;
>      amroutine->aminitparallelscan = NULL;
>      amroutine->amparallelrescan = NULL;
> +    amroutine->amreloptspecset = spggetreloptspecset;
>  
>      PG_RETURN_POINTER(amroutine);
>  }
> @@ -550,6 +550,7 @@ 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.
>       */
> +//elog(WARNING, "fillfactor = %i", SpGistGetFillFactor(index));
>      needSpace += SpGistGetTargetPageFreeSpace(index);
>      needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
>  
> @@ -721,23 +722,6 @@ SpGistInitMetapage(Page page)
>  }
>  
>  /*
> - * reloptions processing for SPGiST
> - */
> -bytea *
> -spgoptions(Datum reloptions, bool validate)
> -{
> -    static const relopt_parse_elt tab[] = {
> -        {"fillfactor", RELOPT_TYPE_INT, offsetof(SpGistOptions, fillfactor)},
> -    };
> -
> -    return (bytea *) build_reloptions(reloptions, validate,
> -                                      RELOPT_KIND_SPGIST,
> -                                      sizeof(SpGistOptions),
> -                                      tab, lengthof(tab));
> -
> -}
> -
> -/*
>   * Get the space needed to store a non-null datum of the indicated type
>   * in an inner tuple (that is, as a prefix or node label).
>   * Note the result is already rounded up to a MAXALIGN boundary.
> @@ -1336,3 +1320,25 @@ spgproperty(Oid index_oid, int attno,
>  
>      return true;
>  }
> +
> +static options_spec_set *spgist_relopt_specset = NULL;
> +
> +void *
> +spggetreloptspecset(void)
> +{
> +    if (!spgist_relopt_specset)
> +    {
> +        spgist_relopt_specset = allocateOptionsSpecSet(NULL,
> +                                                sizeof(SpGistOptions), 1);
> +
> +        optionsSpecSetAddInt(spgist_relopt_specset, "fillfactor",
> +                          "Packs spgist index pages only to this percentage",
> +                                 ShareUpdateExclusiveLock,        /* since it applies only
> +                                                                 * to later inserts */
> +                                 0,
> +                                 offsetof(SpGistOptions, fillfactor),
> +                                 SPGIST_DEFAULT_FILLFACTOR,
> +                                 SPGIST_MIN_FILLFACTOR, 100);
> +    }
> +    return spgist_relopt_specset;
> +}
> diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
> index 0982851..4f3dbb8 100644
> --- a/src/backend/commands/createas.c
> +++ b/src/backend/commands/createas.c
> @@ -90,6 +90,7 @@ create_ctas_internal(List *attrList, IntoClause *into)
>      Datum        toast_options;
>      static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
>      ObjectAddress intoRelationAddr;
> +    List       *toastDefList;
>  
>      /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
>      is_matview = (into->viewQuery != NULL);
> @@ -124,14 +125,12 @@ 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);
> +    optionsDefListValdateNamespaces(create->options, validnsps);
> +    toastDefList = optionsDefListFilterNamespaces(create->options, "toast");
>  
> +    toast_options = transformOptions(get_toast_relopt_spec_set(), (Datum) 0,
> +                                     toastDefList, 0);
>      NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options);
>  
>      /* Create the "view" part of a materialized view. */
> diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
> index 146fa57..758ca34 100644
> --- a/src/backend/commands/foreigncmds.c
> +++ b/src/backend/commands/foreigncmds.c
> @@ -112,7 +112,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 c14ca27..96d465a 100644
> --- a/src/backend/commands/indexcmds.c
> +++ b/src/backend/commands/indexcmds.c
> @@ -19,6 +19,7 @@
>  #include "access/heapam.h"
>  #include "access/htup_details.h"
>  #include "access/reloptions.h"
> +#include "access/options.h"
>  #include "access/sysattr.h"
>  #include "access/tableam.h"
>  #include "access/xact.h"
> @@ -531,7 +532,7 @@ DefineIndex(Oid relationId,
>      Form_pg_am    accessMethodForm;
>      IndexAmRoutine *amRoutine;
>      bool        amcanorder;
> -    amoptions_function amoptions;
> +    amreloptspecset_function amreloptspecsetfn;
>      bool        partitioned;
>      bool        safe_index;
>      Datum        reloptions;
> @@ -837,7 +838,7 @@ DefineIndex(Oid relationId,
>                          accessMethodName)));
>  
>      amcanorder = amRoutine->amcanorder;
> -    amoptions = amRoutine->amoptions;
> +    amreloptspecsetfn = amRoutine->amreloptspecset;
>  
>      pfree(amRoutine);
>      ReleaseSysCache(tuple);
> @@ -851,10 +852,19 @@ 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 (amreloptspecsetfn)
> +    {
> +        reloptions = transformOptions(amreloptspecsetfn(),
> +                                      (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.
> @@ -1986,8 +1996,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
>                      palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
>  
>              indexInfo->ii_OpclassOptions[attn] =
> -                transformRelOptions((Datum) 0, attribute->opclassopts,
> -                                    NULL, NULL, false, false);
> +                optionsDefListToTextArray(attribute->opclassopts);
>          }
>  
>          attn++;
> diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
> index 1c2ebe1..7f3004f 100644
> --- a/src/backend/commands/tablecmds.c
> +++ b/src/backend/commands/tablecmds.c
> @@ -20,6 +20,7 @@
>  #include "access/heapam_xlog.h"
>  #include "access/multixact.h"
>  #include "access/reloptions.h"
> +#include "access/options.h"
>  #include "access/relscan.h"
>  #include "access/sysattr.h"
>  #include "access/tableam.h"
> @@ -641,7 +642,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
>      ListCell   *listptr;
>      AttrNumber    attnum;
>      bool        partitioned;
> -    static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
>      Oid            ofTypeId;
>      ObjectAddress address;
>      LOCKMODE    parentLockmode;
> @@ -789,19 +789,37 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
>      /*
>       * Parse and validate reloptions, if any.
>       */
> -    reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
> -                                     true, false);
>  
>      switch (relkind)
>      {
>          case RELKIND_VIEW:
> -            (void) view_reloptions(reloptions, true);
> +            reloptions = transformOptions(
> +                                      get_view_relopt_spec_set(),
> +                                      (Datum) 0, stmt->options, 0);
>              break;
>          case RELKIND_PARTITIONED_TABLE:
> -            (void) partitioned_table_reloptions(reloptions, true);
> +        {
> +            /* If it is not all listed above, then it if heap */
> +            char       *namespaces[] = HEAP_RELOPT_NAMESPACES;
> +            List       *heapDefList;
> +
> +            optionsDefListValdateNamespaces(stmt->options, namespaces);
> +            heapDefList = optionsDefListFilterNamespaces(stmt->options, NULL);
> +            reloptions = transformOptions(get_partitioned_relopt_spec_set(),
> +                                      (Datum) 0, heapDefList, 0);
>              break;
> +        }
>          default:
> -            (void) heap_reloptions(relkind, reloptions, true);
> +        {
> +            /* If it is not all listed above, 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_spec_set(),
> +                                      (Datum) 0, heapDefList, 0);
> +        }
>      }
>  
>      if (stmt->ofTypename)
> @@ -4022,7 +4040,7 @@ void
>  AlterTableInternal(Oid relid, List *cmds, bool recurse)
>  {
>      Relation    rel;
> -    LOCKMODE    lockmode = AlterTableGetLockLevel(cmds);
> +    LOCKMODE    lockmode = AlterTableGetLockLevel(relid, cmds);
>  
>      rel = relation_open(relid, lockmode);
>  
> @@ -4064,7 +4082,7 @@ 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.
> @@ -4285,9 +4303,13 @@ AlterTableGetLockLevel(List *cmds)
>                                       * getTables() */
>              case AT_ResetRelOptions:    /* Uses MVCC in getIndexes() and
>                                           * getTables() */
> -                cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
> -                break;
> -
> +                {
> +                    Relation rel = relation_open(relid, NoLock);  // FIXME I am not sure how wise it is
> +                    cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
> +                                                    castNode(List, cmd->def));
> +                    relation_close(rel,NoLock);
> +                    break;
> +                }
>              case AT_AttachPartition:
>                  cmd_lockmode = ShareUpdateExclusiveLock;
>                  break;
> @@ -8062,11 +8084,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_spec_set(),
> +                                  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));
> @@ -13704,7 +13726,8 @@ 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;
> +    List       *toastDefList;
> +    options_parse_mode parse_mode;
>  
>      if (defList == NIL && operation != AT_ReplaceRelOptions)
>          return;                    /* nothing to do */
> @@ -13734,27 +13757,68 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
>      }
>  
>      /* 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_TOASTVALUE: // FIXME why it is here???
>          case RELKIND_MATVIEW:
> -            (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_spec_set(),
> +                                              isnull ? (Datum) 0 : datum,
> +                                              heapDefList, parse_mode);
> +            }
>              break;
> +
>          case RELKIND_PARTITIONED_TABLE:
> -            (void) partitioned_table_reloptions(newOptions, true);
> -            break;
> +            {
> +                char       *namespaces[] = HEAP_RELOPT_NAMESPACES;
> +                List       *heapDefList;
> +
> +                optionsDefListValdateNamespaces(defList, namespaces);
> +                heapDefList = optionsDefListFilterNamespaces(
> +                                                             defList, NULL);
> +                newOptions = transformOptions(get_partitioned_relopt_spec_set(),
> +                                              isnull ? (Datum) 0 : datum,
> +                                              heapDefList, parse_mode);
> +                break;
> +            }
>          case RELKIND_VIEW:
> -            (void) view_reloptions(newOptions, true);
> -            break;
> +            {
> +
> +                newOptions = transformOptions(
> +                                      get_view_relopt_spec_set(),
> +                                      datum, defList, parse_mode);
> +                break;
> +            }
>          case RELKIND_INDEX:
>          case RELKIND_PARTITIONED_INDEX:
> -            (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
> +            if (! rel->rd_indam->amreloptspecset)
> +            {
> +                ereport(ERROR,
> +                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
> +                         errmsg("index %s does not support options",
> +                                RelationGetRelationName(rel))));
> +                break;
> +            }
> +            parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;
> +            if (operation == AT_ResetRelOptions)
> +                parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;
> +            newOptions = transformOptions(
> +                                    rel->rd_indam->amreloptspecset(),
> +                                            isnull ? (Datum) 0 : datum,
> +                                            defList, parse_mode);
>              break;
>          default:
>              ereport(ERROR,
> @@ -13769,7 +13833,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;
>  
> @@ -13853,11 +13917,15 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
>                                      &isnull);
>          }
>  
> -        newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
> -                                         defList, "toast", validnsps, false,
> -                                         operation == AT_ResetRelOptions);
> +        parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;
> +        if (operation == AT_ResetRelOptions)
> +            parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;
> +
> +        toastDefList = optionsDefListFilterNamespaces(defList, "toast");
>  
> -        (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
> +        newOptions = transformOptions(get_toast_relopt_spec_set(),
> +                                      isnull ? (Datum) 0 : datum,
> +                                      toastDefList, parse_mode);
>  
>          memset(repl_val, 0, sizeof(repl_val));
>          memset(repl_null, false, sizeof(repl_null));
> diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
> index 4b96eec..912699b 100644
> --- a/src/backend/commands/tablespace.c
> +++ b/src/backend/commands/tablespace.c
> @@ -345,10 +345,9 @@ 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_spec_set(),
> +                                                (Datum) 0, stmt->options, 0);
> +
>      if (newOptions != (Datum) 0)
>          values[Anum_pg_tablespace_spcoptions - 1] = newOptions;
>      else
> @@ -1053,10 +1052,11 @@ 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_spec_set(),
> +                                  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 5564dc3..0370be7 100644
> --- a/src/backend/foreign/foreign.c
> +++ b/src/backend/foreign/foreign.c
> @@ -78,7 +78,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
>      if (isnull)
>          fdw->options = NIL;
>      else
> -        fdw->options = untransformRelOptions(datum);
> +        fdw->options = optionsTextArrayToDefList(datum);
>  
>      ReleaseSysCache(tp);
>  
> @@ -165,7 +165,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
>      if (isnull)
>          server->options = NIL;
>      else
> -        server->options = untransformRelOptions(datum);
> +        server->options = optionsTextArrayToDefList(datum);
>  
>      ReleaseSysCache(tp);
>  
> @@ -233,7 +233,7 @@ GetUserMapping(Oid userid, Oid serverid)
>      if (isnull)
>          um->options = NIL;
>      else
> -        um->options = untransformRelOptions(datum);
> +        um->options = optionsTextArrayToDefList(datum);
>  
>      ReleaseSysCache(tp);
>  
> @@ -270,7 +270,7 @@ GetForeignTable(Oid relid)
>      if (isnull)
>          ft->options = NIL;
>      else
> -        ft->options = untransformRelOptions(datum);
> +        ft->options = optionsTextArrayToDefList(datum);
>  
>      ReleaseSysCache(tp);
>  
> @@ -303,7 +303,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
>      if (isnull)
>          options = NIL;
>      else
> -        options = untransformRelOptions(datum);
> +        options = optionsTextArrayToDefList(datum);
>  
>      ReleaseSysCache(tp);
>  
> @@ -572,7 +572,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;
>  }
> @@ -643,7 +643,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/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
> index 313d7b6..1fe41b4 100644
> --- a/src/backend/parser/parse_utilcmd.c
> +++ b/src/backend/parser/parse_utilcmd.c
> @@ -1757,7 +1757,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
>          /* Add the operator class name, if non-default */
>          iparam->opclass = get_opclass(indclass->values[keyno], keycoltype);
>          iparam->opclassopts =
> -            untransformRelOptions(get_attoptions(source_relid, keyno + 1));
> +            optionsTextArrayToDefList(get_attoptions(source_relid, keyno + 1));
>  
>          iparam->ordering = SORTBY_DEFAULT;
>          iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
> @@ -1821,7 +1821,7 @@ generateClonedIndexStmt(RangeVar *heapRel, 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/tcop/utility.c b/src/backend/tcop/utility.c
> index bf085aa..d12ab1a 100644
> --- a/src/backend/tcop/utility.c
> +++ b/src/backend/tcop/utility.c
> @@ -1155,6 +1155,7 @@ ProcessUtilitySlow(ParseState *pstate,
>                              CreateStmt *cstmt = (CreateStmt *) stmt;
>                              Datum        toast_options;
>                              static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
> +                            List       *toastDefList;
>  
>                              /* Remember transformed RangeVar for LIKE */
>                              table_rv = cstmt->relation;
> @@ -1178,15 +1179,17 @@ ProcessUtilitySlow(ParseState *pstate,
>                               * parse and validate reloptions for the toast
>                               * table
>                               */
> -                            toast_options = transformRelOptions((Datum) 0,
> -                                                                cstmt->options,
> -                                                                "toast",
> -                                                                validnsps,
> -                                                                true,
> -                                                                false);
> -                            (void) heap_reloptions(RELKIND_TOASTVALUE,
> -                                                   toast_options,
> -                                                   true);
> +
> +                            optionsDefListValdateNamespaces(
> +                                              ((CreateStmt *) stmt)->options,
> +                                                            validnsps);
> +
> +                            toastDefList = optionsDefListFilterNamespaces(
> +                                    ((CreateStmt *) stmt)->options, "toast");
> +
> +                            toast_options = transformOptions(
> +                                       get_toast_relopt_spec_set(), (Datum) 0,
> +                                                             toastDefList, 0);
>  
>                              NewRelationCreateToastTable(address.objectId,
>                                                          toast_options);
> @@ -1295,9 +1298,12 @@ 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); // FIXME!
> +                    if (OidIsValid(relid))
> +                    {
> +                        lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
> +                        relid = AlterTableLookupRelation(atstmt, lockmode);
> +                    }
>                      if (OidIsValid(relid))
>                      {
>                          AlterTableUtilityContext atcontext;
> diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
> index 72d89cb..f651129 100644
> --- a/src/backend/utils/cache/attoptcache.c
> +++ b/src/backend/utils/cache/attoptcache.c
> @@ -16,6 +16,7 @@
>   */
>  #include "postgres.h"
>  
> +#include "access/options.h"
>  #include "access/reloptions.h"
>  #include "utils/attoptcache.h"
>  #include "utils/catcache.h"
> @@ -148,7 +149,8 @@ get_attribute_options(Oid attrelid, int attnum)
>                  opts = NULL;
>              else
>              {
> -                bytea       *bytea_opts = attribute_reloptions(datum, false);
> +                bytea   *bytea_opts = optionsTextArrayToBytea(
> +                                    get_attribute_options_spec_set(), datum, 0);
>  
>                  opts = MemoryContextAlloc(CacheMemoryContext,
>                                            VARSIZE(bytea_opts));
> diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
> index 13d9994..f22c2d9 100644
> --- a/src/backend/utils/cache/relcache.c
> +++ b/src/backend/utils/cache/relcache.c
> @@ -441,7 +441,7 @@ static void
>  RelationParseRelOptions(Relation relation, HeapTuple tuple)
>  {
>      bytea       *options;
> -    amoptions_function amoptsfn;
> +    amreloptspecset_function amoptspecsetfn;
>  
>      relation->rd_options = NULL;
>  
> @@ -456,11 +456,11 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
>          case RELKIND_VIEW:
>          case RELKIND_MATVIEW:
>          case RELKIND_PARTITIONED_TABLE:
> -            amoptsfn = NULL;
> +            amoptspecsetfn = NULL;
>              break;
>          case RELKIND_INDEX:
>          case RELKIND_PARTITIONED_INDEX:
> -            amoptsfn = relation->rd_indam->amoptions;
> +            amoptspecsetfn = relation->rd_indam->amreloptspecset;
>              break;
>          default:
>              return;
> @@ -471,7 +471,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
>       * we might not have any other for pg_class yet (consider executing this
>       * code for pg_class itself)
>       */
> -    options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
> +    options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
>  
>      /*
>       * 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 5870f43..87f2fa5 100644
> --- a/src/backend/utils/cache/spccache.c
> +++ b/src/backend/utils/cache/spccache.c
> @@ -148,7 +148,8 @@ get_tablespace(Oid spcid)
>              opts = NULL;
>          else
>          {
> -            bytea       *bytea_opts = tablespace_reloptions(datum, false);
> +            bytea *bytea_opts  = optionsTextArrayToBytea(
> +                                get_tablespace_options_spec_set(), datum, 0);
>  
>              opts = MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts));
>              memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
> diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
> index d357ebb..b8fb6b9 100644
> --- a/src/include/access/amapi.h
> +++ b/src/include/access/amapi.h
> @@ -136,10 +136,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,
> @@ -186,6 +182,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
>  /* restore marked scan position */
>  typedef void (*amrestrpos_function) (IndexScanDesc scan);
>  
> +/* get catalog of reloptions definitions */
> +typedef void *(*amreloptspecset_function) ();
> +
>  /*
>   * Callback function signatures - for parallel index scans.
>   */
> @@ -263,7 +262,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 */
>      ambuildphasename_function ambuildphasename; /* can be NULL */
>      amvalidate_function amvalidate;
> @@ -275,6 +273,7 @@ typedef struct IndexAmRoutine
>      amendscan_function amendscan;
>      ammarkpos_function ammarkpos;    /* can be NULL */
>      amrestrpos_function amrestrpos; /* can be NULL */
> +    amreloptspecset_function amreloptspecset; /* 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 4e2be13..25b3456 100644
> --- a/src/include/access/brin.h
> +++ b/src/include/access/brin.h
> @@ -36,6 +36,8 @@ typedef struct BrinStatsData
>  
>  
>  #define BRIN_DEFAULT_PAGES_PER_RANGE    128
> +#define BRIN_MIN_PAGES_PER_RANGE        1
> +#define BRIN_MAX_PAGES_PER_RANGE        131072
>  #define BrinGetPagesPerRange(relation) \
>      (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
>                   relation->rd_rel->relam == BRIN_AM_OID), \
> diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
> index 79440eb..a798a96 100644
> --- a/src/include/access/brin_internal.h
> +++ b/src/include/access/brin_internal.h
> @@ -14,6 +14,7 @@
>  #include "access/amapi.h"
>  #include "storage/bufpage.h"
>  #include "utils/typcache.h"
> +#include "access/options.h"
>  
>  
>  /*
> @@ -108,6 +109,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
>  extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
>                                                  IndexBulkDeleteResult *stats);
>  extern bytea *brinoptions(Datum reloptions, bool validate);
> +extern void * bringetreloptspecset (void);
>  
>  /* 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 670a40b..2b7c25c 100644
> --- a/src/include/access/gin_private.h
> +++ b/src/include/access/gin_private.h
> @@ -108,6 +108,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 *gingetreloptspecset(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 553d364..015b75a 100644
> --- a/src/include/access/gist_private.h
> +++ b/src/include/access/gist_private.h
> @@ -22,6 +22,7 @@
>  #include "storage/buffile.h"
>  #include "utils/hsearch.h"
>  #include "access/genam.h"
> +#include "access/reloptions.h" //FIXME! should be replaced with options.h finally
>  
>  /*
>   * Maximum number of "halves" a page can be split into in one operation.
> @@ -388,6 +389,7 @@ typedef enum GistOptBufferingMode
>      GIST_OPTION_BUFFERING_OFF
>  } GistOptBufferingMode;
>  
> +
>  /*
>   * Storage type for GiST's reloptions
>   */
> @@ -478,7 +480,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
>  #define GIST_MIN_FILLFACTOR            10
>  #define GIST_DEFAULT_FILLFACTOR        90
>  
> -extern bytea *gistoptions(Datum reloptions, bool validate);
> +extern void *gistgetreloptspecset(void);
>  extern bool gistproperty(Oid index_oid, int attno,
>                           IndexAMProperty prop, const char *propname,
>                           bool *res, bool *isnull);
> diff --git a/src/include/access/hash.h b/src/include/access/hash.h
> index 1cce865..91922ef 100644
> --- a/src/include/access/hash.h
> +++ b/src/include/access/hash.h
> @@ -378,7 +378,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 void hashadjustmembers(Oid opfamilyoid,
>                                Oid opclassoid,
> @@ -470,6 +469,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
>  extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
>                                                   uint32 lowmask, uint32 maxbucket);
>  extern void _hash_kill_items(IndexScanDesc scan);
> +extern void *hashgetreloptspecset(void);
>  
>  /* hash.c */
>  extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
> diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
> index 30a216e..1fcb5f5 100644
> --- a/src/include/access/nbtree.h
> +++ b/src/include/access/nbtree.h
> @@ -1252,7 +1252,7 @@ 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 void * btgetreloptspecset (void);
>  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..34e2917
> --- /dev/null
> +++ b/src/include/access/options.h
> @@ -0,0 +1,245 @@
> +/*-------------------------------------------------------------------------
> + *
> + * 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"
> +
> +
> +/* supported option types */
> +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_spec_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_spec_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;
> +
> +
> +
> +/*
> + * opt_enum_elt_def -- One member of the array of acceptable values
> + * of an enum reloption.
> + */
> +typedef struct opt_enum_elt_def
> +{
> +    const char *string_val;
> +    int            symbol_val;
> +} opt_enum_elt_def;
> +
> +
> +/* generic structure to store Option Spec information */
> +typedef struct option_spec_basic
> +{
> +    const char *name;            /* must be first (used as list termination
> +                                 * marker) */
> +    const char *desc;
> +    LOCKMODE    lockmode;
> +    option_spec_flags flags;
> +    option_type type;
> +    int            struct_offset;    /* offset of the value in Bytea representation */
> +}    option_spec_basic;
> +
> +
> +/* reloptions records for specific variable types */
> +typedef struct option_spec_bool
> +{
> +    option_spec_basic base;
> +    bool        default_val;
> +}    option_spec_bool;
> +
> +typedef struct option_spec_int
> +{
> +    option_spec_basic base;
> +    int            default_val;
> +    int            min;
> +    int            max;
> +}    option_spec_int;
> +
> +typedef struct option_spec_real
> +{
> +    option_spec_basic base;
> +    double        default_val;
> +    double        min;
> +    double        max;
> +}    option_spec_real;
> +
> +typedef struct option_spec_enum
> +{
> +    option_spec_basic base;
> +    opt_enum_elt_def *members;/* FIXME rewrite. Null terminated array of allowed values for
> +                                 * the option */
> +    int            default_val;    /* Number of item of allowed_values array */
> +    const char  *detailmsg;
> +}    option_spec_enum;
> +
> +/* validation routines for strings */
> +typedef void (*validate_string_option) (const char *value);
> +
> +/*
> + * When storing sting reloptions, we shoud deal with special case when
> + * option value is not set. For fixed length options, we just copy default
> + * option value into the binary structure. For varlen value, there can be
> + * "not set" special case, with no default value offered.
> + * In this case we will set offset value to -1, so code that use relptions
> + * can deal this case. For better readability it was defined as a constant.
> + */
> +#define OPTION_STRING_VALUE_NOT_SET_OFFSET -1
> +
> +typedef struct option_spec_string
> +{
> +    option_spec_basic base;
> +    validate_string_option validate_cb;
> +    char       *default_val;
> +}    option_spec_string;
> +
> +typedef void (*postprocess_bytea_options_function) (void *data, bool validate);
> +
> +typedef struct options_spec_set
> +{
> +    option_spec_basic **definitions;
> +    int            num;            /* Number of spec_set items in use */
> +    int            num_allocated;    /* Number of spec_set items allocated */
> +    bool        forbid_realloc; /* If number of items of the spec_set were
> +                                 * strictly set to certain value do no allow
> +                                 * adding more idems */
> +    Size        struct_size;    /* Size of a structure for options in binary
> +                                 * representation */
> +    postprocess_bytea_options_function postprocess_fun; /* This function is
> +                                                         * called after options
> +                                                         * were converted in
> +                                                         * Bytea represenation.
> +                                                         * Can be used for extra
> +                                                         * validation and so on */
> +    char       *namespace;        /* spec_set is used for options from this
> +                                 * namespase */
> +}    options_spec_set;
> +
> +
> +/* holds an option value parsed or unparsed */
> +typedef struct option_value
> +{
> +    option_spec_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;
> +
> +
> +
> +
> +/*
> + * Options spec_set related functions
> + */
> +extern options_spec_set *allocateOptionsSpecSet(const char *namespace,
> +                                 int size_of_bytea, int num_items_expected);
> +extern void optionsSpecSetAddBool(options_spec_set * spec_set, const char *name,
> +                  const char *desc, LOCKMODE lockmode, option_spec_flags flags,
> +                                    int struct_offset, bool default_val);
> +extern void optionsSpecSetAddInt(options_spec_set * spec_set, const char *name,
> +                    const char *desc, LOCKMODE lockmode, option_spec_flags flags,
> +                    int struct_offset, int default_val, int min_val, int max_val);
> +extern void optionsSpecSetAddReal(options_spec_set * spec_set, const char *name,
> +          const char *desc, LOCKMODE lockmode, option_spec_flags flags,
> +      int struct_offset, double default_val, double min_val, double max_val);
> +extern void optionsSpecSetAddEnum(options_spec_set * spec_set,
> +                          const char *name, const char *desc, LOCKMODE lockmode, option_spec_flags flags,
> +            int struct_offset, opt_enum_elt_def* members, int default_val, const char *detailmsg);
> +extern void optionsSpecSetAddString(options_spec_set * spec_set, const char *name,
> +          const char *desc, LOCKMODE lockmode, option_spec_flags flags,
> +int struct_offset, const char *default_val, validate_string_option validator);
> +
> +
> +/*
> + * This macro allows to get string option value from bytea representation.
> + * "optstruct" - is a structure that is stored in bytea options representation
> + * "member" - member of this structure that has string option value
> + * (actually string values are stored in bytea after the structure, and
> + * and "member" will contain an offset to this value. This macro do all
> + * the math
> + */
> +#define GET_STRING_OPTION(optstruct, member) \
> +    ((optstruct)->member == OPTION_STRING_VALUE_NOT_SET_OFFSET ? NULL : \
> +     (char *)(optstruct) + (optstruct)->member)
> +
> +/*
> + * Functions related to option convertation, parsing, manipulation
> + * and validation
> + */
> +extern void optionsDefListValdateNamespaces(List *defList,
> +                                char **allowed_namespaces);
> +extern List *optionsDefListFilterNamespaces(List *defList, const char *namespace);
> +extern List *optionsTextArrayToDefList(Datum options);
> +extern Datum optionsDefListToTextArray(List *defList);
> +/*
> + * Meta functions that uses functions above to get options for relations,
> + * tablespaces, views and so on
> + */
> +
> +extern bytea *optionsTextArrayToBytea(options_spec_set * spec_set, Datum data,
> +                                                                bool validate);
> +extern Datum transformOptions(options_spec_set * spec_set, 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 7c5fbeb..21b91df 100644
> --- a/src/include/access/reloptions.h
> +++ b/src/include/access/reloptions.h
> @@ -22,6 +22,7 @@
>  #include "access/amapi.h"
>  #include "access/htup.h"
>  #include "access/tupdesc.h"
> +#include "access/options.h"
>  #include "nodes/pg_list.h"
>  #include "storage/lock.h"
>  
> @@ -110,20 +111,10 @@ typedef struct relopt_real
>      double        max;
>  } relopt_real;
>  
> -/*
> - * relopt_enum_elt_def -- One member of the array of acceptable values
> - * of an enum reloption.
> - */
> -typedef struct relopt_enum_elt_def
> -{
> -    const char *string_val;
> -    int            symbol_val;
> -} relopt_enum_elt_def;
> -
>  typedef struct relopt_enum
>  {
>      relopt_gen    gen;
> -    relopt_enum_elt_def *members;
> +    opt_enum_elt_def *members;
>      int            default_val;
>      const char *detailmsg;
>      /* null-terminated array of members */
> @@ -167,6 +158,7 @@ typedef struct local_relopts
>      List       *options;        /* list of local_relopt definitions */
>      List       *validators;        /* list of relopts_validator callbacks */
>      Size        relopt_struct_size; /* size of parsed bytea structure */
> +    options_spec_set * spec_set; /* FIXME */
>  } local_relopts;
>  
>  /*
> @@ -179,21 +171,6 @@ typedef struct local_relopts
>      ((optstruct)->member == 0 ? NULL : \
>       (char *)(optstruct) + (optstruct)->member)
>  
> -extern relopt_kind add_reloption_kind(void);
> -extern void add_bool_reloption(bits32 kinds, const char *name, const char *desc,
> -                               bool default_val, LOCKMODE lockmode);
> -extern void add_int_reloption(bits32 kinds, const char *name, const char *desc,
> -                              int default_val, int min_val, int max_val,
> -                              LOCKMODE lockmode);
> -extern void add_real_reloption(bits32 kinds, const char *name, const char *desc,
> -                               double default_val, double min_val, double max_val,
> -                               LOCKMODE lockmode);
> -extern void add_enum_reloption(bits32 kinds, const char *name, const char *desc,
> -                               relopt_enum_elt_def *members, int default_val,
> -                               const char *detailmsg, LOCKMODE lockmode);
> -extern void add_string_reloption(bits32 kinds, const char *name, const char *desc,
> -                                 const char *default_val, validate_string_relopt validator,
> -                                 LOCKMODE lockmode);
>  
>  extern void init_local_reloptions(local_relopts *opts, Size relopt_struct_size);
>  extern void register_reloptions_validator(local_relopts *opts,
> @@ -210,7 +187,7 @@ extern void add_local_real_reloption(local_relopts *opts, const char *name,
>                                       int offset);
>  extern void add_local_enum_reloption(local_relopts *relopts,
>                                       const char *name, const char *desc,
> -                                     relopt_enum_elt_def *members,
> +                                     opt_enum_elt_def *members,
>                                       int default_val, const char *detailmsg,
>                                       int offset);
>  extern void add_local_string_reloption(local_relopts *opts, const char *name,
> @@ -219,29 +196,17 @@ extern void add_local_string_reloption(local_relopts *opts, const char *name,
>                                         validate_string_relopt validator,
>                                         fill_string_relopt filler, int offset);
>  
> -extern Datum transformRelOptions(Datum oldOptions, List *defList,
> -                                 const char *namspace, char *validnsps[],
> -                                 bool acceptOidsOff, bool isReset);
> -extern List *untransformRelOptions(Datum options);
>  extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
> -                                amoptions_function amoptions);
> -extern void *build_reloptions(Datum reloptions, bool validate,
> -                              relopt_kind kind,
> -                              Size relopt_struct_size,
> -                              const relopt_parse_elt *relopt_elems,
> -                              int num_relopt_elems);
> +                                amreloptspecset_function amoptions_def_set);
>  extern void *build_local_reloptions(local_relopts *relopts, Datum options,
>                                      bool validate);
>  
> -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 *partitioned_table_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);
> +options_spec_set *get_heap_relopt_spec_set(void);
> +options_spec_set *get_toast_relopt_spec_set(void);
> +options_spec_set *get_partitioned_relopt_spec_set(void);
> +options_spec_set *get_view_relopt_spec_set(void);
> +options_spec_set *get_attribute_options_spec_set(void);
> +options_spec_set *get_tablespace_options_spec_set(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 2eb2f42..d9a9b2d 100644
> --- a/src/include/access/spgist.h
> +++ b/src/include/access/spgist.h
> @@ -189,9 +189,6 @@ typedef struct spgLeafConsistentOut
>  } spgLeafConsistentOut;
>  
>  
> -/* spgutils.c */
> -extern bytea *spgoptions(Datum reloptions, bool validate);
> -
>  /* spginsert.c */
>  extern IndexBuildResult *spgbuild(Relation heap, Relation index,
>                                    struct IndexInfo *indexInfo);
> diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
> index 40d3b71..dd9a05a 100644
> --- a/src/include/access/spgist_private.h
> +++ b/src/include/access/spgist_private.h
> @@ -529,6 +529,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page,
>  extern bool spgproperty(Oid index_oid, int attno,
>                          IndexAMProperty prop, const char *propname,
>                          bool *res, bool *isnull);
> +extern void *spggetreloptspecset(void);
>  
>  /* spgdoinsert.c */
>  extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
> diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
> index 336549c..3f87f98 100644
> --- a/src/include/commands/tablecmds.h
> +++ b/src/include/commands/tablecmds.h
> @@ -34,7 +34,7 @@ extern Oid    AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
>  extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
>                         struct AlterTableUtilityContext *context);
>  
> -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/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
> index 5365b063..80b39e8 100644
> --- a/src/test/modules/dummy_index_am/dummy_index_am.c
> +++ b/src/test/modules/dummy_index_am/dummy_index_am.c
> @@ -14,7 +14,7 @@
>  #include "postgres.h"
>  
>  #include "access/amapi.h"
> -#include "access/reloptions.h"
> +#include "access/options.h"
>  #include "catalog/index.h"
>  #include "commands/vacuum.h"
>  #include "nodes/pathnodes.h"
> @@ -25,12 +25,6 @@ PG_MODULE_MAGIC;
>  
>  void        _PG_init(void);
>  
> -/* parse table for fillRelOptions */
> -relopt_parse_elt di_relopt_tab[6];
> -
> -/* Kind of relation options for dummy index */
> -relopt_kind di_relopt_kind;
> -
>  typedef enum DummyAmEnum
>  {
>      DUMMY_AM_ENUM_ONE,
> @@ -49,7 +43,7 @@ typedef struct DummyIndexOptions
>      int            option_string_null_offset;
>  }            DummyIndexOptions;
>  
> -relopt_enum_elt_def dummyAmEnumValues[] =
> +opt_enum_elt_def dummyAmEnumValues[] =
>  {
>      {"one", DUMMY_AM_ENUM_ONE},
>      {"two", DUMMY_AM_ENUM_TWO},
> @@ -63,77 +57,85 @@ PG_FUNCTION_INFO_V1(dihandler);
>   * Validation function for string relation options.
>   */
>  static void
> -validate_string_option(const char *value)
> +divalidate_string_option(const char *value)
>  {
>      ereport(NOTICE,
>              (errmsg("new option value for string parameter %s",
>                      value ? value : "NULL")));
>  }
>  
> -/*
> - * This function creates a full set of relation option types,
> - * with various patterns.
> - */
> -static void
> -create_reloptions_table(void)
> +static options_spec_set *di_relopt_specset = NULL;
> +void * digetreloptspecset(void);
> +
> +void *
> +digetreloptspecset(void)
>  {
> -    di_relopt_kind = add_reloption_kind();
> -
> -    add_int_reloption(di_relopt_kind, "option_int",
> -                      "Integer option for dummy_index_am",
> -                      10, -10, 100, AccessExclusiveLock);
> -    di_relopt_tab[0].optname = "option_int";
> -    di_relopt_tab[0].opttype = RELOPT_TYPE_INT;
> -    di_relopt_tab[0].offset = offsetof(DummyIndexOptions, option_int);
> -
> -    add_real_reloption(di_relopt_kind, "option_real",
> -                       "Real option for dummy_index_am",
> -                       3.1415, -10, 100, AccessExclusiveLock);
> -    di_relopt_tab[1].optname = "option_real";
> -    di_relopt_tab[1].opttype = RELOPT_TYPE_REAL;
> -    di_relopt_tab[1].offset = offsetof(DummyIndexOptions, option_real);
> -
> -    add_bool_reloption(di_relopt_kind, "option_bool",
> -                       "Boolean option for dummy_index_am",
> -                       true, AccessExclusiveLock);
> -    di_relopt_tab[2].optname = "option_bool";
> -    di_relopt_tab[2].opttype = RELOPT_TYPE_BOOL;
> -    di_relopt_tab[2].offset = offsetof(DummyIndexOptions, option_bool);
> -
> -    add_enum_reloption(di_relopt_kind, "option_enum",
> -                       "Enum option for dummy_index_am",
> -                       dummyAmEnumValues,
> -                       DUMMY_AM_ENUM_ONE,
> -                       "Valid values are \"one\" and \"two\".",
> -                       AccessExclusiveLock);
> -    di_relopt_tab[3].optname = "option_enum";
> -    di_relopt_tab[3].opttype = RELOPT_TYPE_ENUM;
> -    di_relopt_tab[3].offset = offsetof(DummyIndexOptions, option_enum);
> -
> -    add_string_reloption(di_relopt_kind, "option_string_val",
> -                         "String option for dummy_index_am with non-NULL default",
> -                         "DefaultValue", &validate_string_option,
> -                         AccessExclusiveLock);
> -    di_relopt_tab[4].optname = "option_string_val";
> -    di_relopt_tab[4].opttype = RELOPT_TYPE_STRING;
> -    di_relopt_tab[4].offset = offsetof(DummyIndexOptions,
> -                                       option_string_val_offset);
> +    if (di_relopt_specset)
> +        return di_relopt_specset;
> +
> +    di_relopt_specset = allocateOptionsSpecSet(NULL,
> +                                               sizeof(DummyIndexOptions), 6);
> +
> +    optionsSpecSetAddInt(
> +        di_relopt_specset, "option_int",
> +        "Integer option for dummy_index_am",
> +        AccessExclusiveLock,
> +        0, offsetof(DummyIndexOptions, option_int),
> +        10, -10, 100
> +    );
> +
> +
> +    optionsSpecSetAddReal(
> +        di_relopt_specset, "option_real",
> +        "Real option for dummy_index_am",
> +        AccessExclusiveLock,
> +        0, offsetof(DummyIndexOptions, option_real),
> +        3.1415, -10, 100
> +    );
> +
> +    optionsSpecSetAddBool(
> +        di_relopt_specset, "option_bool",
> +        "Boolean option for dummy_index_am",
> +        AccessExclusiveLock,
> +        0, offsetof(DummyIndexOptions, option_bool), true
> +    );
> +
> +    optionsSpecSetAddEnum(di_relopt_specset, "option_enum",
> +        "Enum option for dummy_index_am",
> +        AccessExclusiveLock,
> +        0,
> +        offsetof(DummyIndexOptions, option_enum),
> +        dummyAmEnumValues,
> +        DUMMY_AM_ENUM_ONE,
> +        "Valid values are \"one\" and \"two\"."
> +    );
> +
> +    optionsSpecSetAddString(di_relopt_specset, "option_string_val",
> +        "String option for dummy_index_am with non-NULL default",
> +        AccessExclusiveLock,
> +        0,
> +        offsetof(DummyIndexOptions, option_string_val_offset),
> +        "DefaultValue", &divalidate_string_option
> +    );
>  
>      /*
>       * String option for dummy_index_am with NULL default, and without
>       * description.
>       */
> -    add_string_reloption(di_relopt_kind, "option_string_null",
> -                         NULL,    /* description */
> -                         NULL, &validate_string_option,
> -                         AccessExclusiveLock);
> -    di_relopt_tab[5].optname = "option_string_null";
> -    di_relopt_tab[5].opttype = RELOPT_TYPE_STRING;
> -    di_relopt_tab[5].offset = offsetof(DummyIndexOptions,
> -                                       option_string_null_offset);
> +
> +    optionsSpecSetAddString(di_relopt_specset, "option_string_null",
> +        NULL,    /* description */
> +        AccessExclusiveLock,
> +        0,
> +        offsetof(DummyIndexOptions, option_string_null_offset),
> +        NULL, &divalidate_string_option
> +    );
> +
> +    return di_relopt_specset;
>  }
>  
>  
> +
>  /*
>   * Build a new index.
>   */
> @@ -219,19 +221,6 @@ dicostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
>  }
>  
>  /*
> - * Parse relation options for index AM, returning a DummyIndexOptions
> - * structure filled with option values.
> - */
> -static bytea *
> -dioptions(Datum reloptions, bool validate)
> -{
> -    return (bytea *) build_reloptions(reloptions, validate,
> -                                      di_relopt_kind,
> -                                      sizeof(DummyIndexOptions),
> -                                      di_relopt_tab, lengthof(di_relopt_tab));
> -}
> -
> -/*
>   * Validator for index AM.
>   */
>  static bool
> @@ -308,7 +297,6 @@ dihandler(PG_FUNCTION_ARGS)
>      amroutine->amvacuumcleanup = divacuumcleanup;
>      amroutine->amcanreturn = NULL;
>      amroutine->amcostestimate = dicostestimate;
> -    amroutine->amoptions = dioptions;
>      amroutine->amproperty = NULL;
>      amroutine->ambuildphasename = NULL;
>      amroutine->amvalidate = divalidate;
> @@ -322,12 +310,7 @@ dihandler(PG_FUNCTION_ARGS)
>      amroutine->amestimateparallelscan = NULL;
>      amroutine->aminitparallelscan = NULL;
>      amroutine->amparallelrescan = NULL;
> +    amroutine->amreloptspecset = digetreloptspecset;
>  
>      PG_RETURN_POINTER(amroutine);
>  }
> -
> -void
> -_PG_init(void)
> -{
> -    create_reloptions_table();
> -}


-- 
  Bruce Momjian  <bruce@momjian.us>        https://momjian.us
  EDB                                      https://enterprisedb.com

  If only the physical world exists, free will is an illusion.




Re: Suggestion: Unified options API. Need help from core team

От
Nikolay Shaplov
Дата:
В письме от вторник, 26 октября 2021 г. 17:25:32 MSK пользователь Bruce
Momjian написал:
> Uh, the core team does not get involved in development issues, unless
> there is a issue that clearly cannot be resolved by discussion on the
> hackers list.
Then may be I used wrong therm. May be I should say "experienced postgres
developers".

>
> ---------------------------------------------------------------------------
>
> On Mon, Oct 18, 2021 at 04:24:23PM +0300, Nikolay Shaplov wrote:
> > Hi!
> >
> > I am still hoping to finish my work on reloptions I've started some years
> > ago.
> >
> > I've renewed my patch and I think I need help from core team to finish it.
> >
> > General idea of the patch: Now we have three ways to define options for
> > different objects, with more or less different code used for it. It wold
> > be
> > better to have unified context independent API for processing options,
> > instead.
> >
> > Long story short:
> >
> > There is Option Specification object, that has all information about
> > single
> > option, how it should be parsed and validated.
> >
> > There is Option Specification Set object, an array of Option Specs, that
> > defines all options available for certain object (am of some index for
> > example).
> >
> > When some object (relation, opclass,  etc) wants to have an options, it
> > creates an Option Spec Set for there options, and uses it for converting
> > options between different representations (to get is from SQL, to store it
> > in pg_class, to pass it to the core code as bytea etc)
> >
> > For indexes Option Spec Set is available via Access Method API.
> >
> > For non-index relations all Option Spec Sets are left in reloption.c file,
> > and should be moved to heap AM later. (They are not in AM now so will not
> > change it now)
> >
> > Main problem:
> >
> > There are LockModes. LockModes for options is also stored in Option Spec
> > Set. For indexes Option Spec Sec is accessable via AM. So to get LockMode
> > for option of an index you need to have access for it's relation object
> > (so you can call proper AM method to fetch spec set). So you need
> > "Relation rel" in AlterTableGetRelOptionsLockLevel where Lock Level is
> > determinated (src/ backend/access/common/reloptions.c)
> > AlterTableGetRelOptionsLockLevel is called from AlterTableGetLockLevel
> > (src/ backend/commands/tablecmds.c) so we need "Relation rel" there too.
> > AlterTableGetLockLevel is called from AlterTableInternal (/src/backend/
> > commands/tablecmds.c) There we have "Oid relid" so we can try to open
> > relation like this
> >
> >                    Relation rel = relation_open(relid, NoLock);
> >                    cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
> >
> >                                                    castNode(List,
> >                                                    cmd->def));
> >
> >                    relation_close(rel,NoLock);
> >                    break;
> >
> > but this will trigger the assertion
> >
> >    Assert(lockmode != NoLock ||
> >
> >           IsBootstrapProcessingMode() ||
> >           CheckRelationLockedByMe(r, c, true));
> >
> > in relation_open (b/src/backend/access/common/relation.c)
> >
> > For now I've commented this assertion out. I've tried to open relation
> > with
> > AccessShareLock but this caused one test to fail, and I am not sure this
> > solution is better.
> >
> > What I have done here I consider a hack, so I need a help of core-team
> > here to do it in right way.
> >
> > General problems:
> >
> > I guess I need a coauthor, or supervisor from core team, to finish this
> > patch. The amount of code is big, and I guess there are parts that can be
> > made more in postgres way, then I did them. And I would need an advice
> > there, and I guess it would be better to do if before sending it to
> > commitfest.
> >
> >
> > Current patch status:
> >
> > 1. It is Beta. Some minor issues and FIXMEs are not solved. Some code
> > comments needs revising, but in general it do what it is intended to do.
> >
> > 2. This patch does not intend to change postgres behavior at all, all
> > should work as before, all changes are internal only.
> >
> > The only exception is error message for unexciting option name in toast
> > namespace
> >
> >  CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option =
> >  42);>
> > -ERROR:  unrecognized parameter "not_existing_option"
> > +ERROR:  unrecognized parameter "toast.not_existing_option"
> >
> > New message is better I guess, though I can change it back if needed.
> >
> > 3. I am doing my development in this blanch
> > https://gitlab.com/dhyannataraj/ postgres/-/tree/new_options_take_two I
> > am making changes every day, so last version will be available there
> >
> > Would be glad to hear from coreteam before I finish with this patch and
> > made it ready for commit-fest.
> >
> >
> >
> > diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
> > index a22a6df..8f2d5e7 100644
> > --- a/contrib/bloom/bloom.h
> > +++ b/contrib/bloom/bloom.h
> > @@ -17,6 +17,7 @@
> >
> >  #include "access/generic_xlog.h"
> >  #include "access/itup.h"
> >  #include "access/xlog.h"
> >
> > +#include "access/options.h"
> >
> >  #include "fmgr.h"
> >  #include "nodes/pathnodes.h"
> >
> > @@ -207,7 +208,8 @@ extern IndexBulkDeleteResult
> > *blbulkdelete(IndexVacuumInfo *info,>
> >
   void *callback_state);
> >
> >  extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
> >
> >
      IndexBulkDeleteResult *stats);
> >
> > -extern bytea *bloptions(Datum reloptions, bool validate);
> > +extern void *blrelopt_specset(void);
> > +extern void blReloptionPostprocess(void *, bool validate);
> >
> >  extern void blcostestimate(PlannerInfo *root, IndexPath *path,
> >
> >                             double loop_count, Cost
*indexStartupCost,
> >                             Cost *indexTotalCost,
Selectivity *indexSelectivity,
> >
> > diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
> > index 754de00..54dad16 100644
> > --- a/contrib/bloom/blutils.c
> > +++ b/contrib/bloom/blutils.c
> > @@ -15,7 +15,7 @@
> >
> >  #include "access/amapi.h"
> >  #include "access/generic_xlog.h"
> >
> > -#include "access/reloptions.h"
> > +#include "access/options.h"
> >
> >  #include "bloom.h"
> >  #include "catalog/index.h"
> >  #include "commands/vacuum.h"
> >
> > @@ -34,53 +34,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_spec_set *bl_relopt_specset;
> >
> >  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,
> > -                      AccessExclusiveLock);
> > -    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,
> > -                          AccessExclusiveLock);
> > -        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; -    }
> > -}
> > -
> > -/*
> >
> >   * Construct a default set of Bloom options.
> >   */
> >
> >  static BloomOptions *
> >
> > @@ -135,7 +95,7 @@ blhandler(PG_FUNCTION_ARGS)
> >
> >      amroutine->amvacuumcleanup = blvacuumcleanup;
> >      amroutine->amcanreturn = NULL;
> >      amroutine->amcostestimate = blcostestimate;
> >
> > -    amroutine->amoptions = bloptions;
> > +    amroutine->amreloptspecset = blrelopt_specset;
> >
> >      amroutine->amproperty = NULL;
> >      amroutine->ambuildphasename = NULL;
> >      amroutine->amvalidate = blvalidate;
> >
> > @@ -154,6 +114,28 @@ blhandler(PG_FUNCTION_ARGS)
> >
> >      PG_RETURN_POINTER(amroutine);
> >
> >  }
> >
> > +void
> > +blReloptionPostprocess(void *data, bool validate)
> > +{
> > +    BloomOptions *opts = (BloomOptions *) data;
> > +    int            i;
> > +
> > +    if (validate)
> > +        for (i = 0; i < INDEX_MAX_KEYS; i++)
> > +        {
> > +            if (opts->bitSize[i] >= opts->bloomLength)
> > +            {
> > +                ereport(ERROR,
> > +
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > +                       errmsg("col%i should not be grater
than length", i)));
> > +            }
> > +        }
> > +
> > +    /* Convert signature length from # of bits to # to words, rounding up
*/
> > +    opts->bloomLength = (opts->bloomLength + SIGNWORDBITS - 1) /
> > SIGNWORDBITS; +}
> > +
> > +
> >
> >  /*
> >
> >   * Fill BloomState structure for particular index.
> >   */
> >
> > @@ -474,24 +456,39 @@ BloomInitMetapage(Relation index)
> >
> >      UnlockReleaseBuffer(metaBuffer);
> >
> >  }
> >
> > -/*
> > - * Parse reloptions for bloom index, producing a BloomOptions struct.
> > - */
> > -bytea *
> > -bloptions(Datum reloptions, bool validate)
> > +void *
> > +blrelopt_specset(void)
> >
> >  {
> >
> > -    BloomOptions *rdopts;
> > +    int            i;
> > +    char        buf[16];
> >
> > -    /* Parse the user-given reloptions */
> > -    rdopts = (BloomOptions *) build_reloptions(reloptions, validate,
> > -
       bl_relopt_kind,
> > -
       sizeof(BloomOptions),
> > -
       bl_relopt_tab,
> > -
       lengthof(bl_relopt_tab));
> > +    if (bl_relopt_specset)
> > +        return bl_relopt_specset;
> >
> > -    /* Convert signature length from # of bits to # to words, rounding
up */
> > -    if (rdopts)
> > -        rdopts->bloomLength = (rdopts->bloomLength + SIGNWORDBITS -
1) /
> > SIGNWORDBITS;
> >
> > -    return (bytea *) rdopts;
> > +    bl_relopt_specset = allocateOptionsSpecSet(NULL,
> > +
sizeof(BloomOptions), INDEX_MAX_KEYS + 1);
> > +    bl_relopt_specset->postprocess_fun = blReloptionPostprocess;
> > +
> > +    optionsSpecSetAddInt(bl_relopt_specset, "length",
> > +                             "Length of signature
in bits",
> > +                             NoLock,        /*
No lock as far as ALTER is
> > +
     * forbidden */
> > +                             0,
> > +
offsetof(BloomOptions, bloomLength),
> > +
DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH);
> > +
> > +    /* 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);
> > +        optionsSpecSetAddInt(bl_relopt_specset, buf,
> > +                               "Number of bits
for corresponding column",
> > +                                 NoLock,    /*
No lock as far as ALTER is
> > +
     * forbidden */
> > +                                 0,
> > +
offsetof(BloomOptions, bitSize[i]),
> > +
DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS);
> > +    }
> > +    return bl_relopt_specset;
> >
> >  }
> >
> > diff --git a/contrib/bloom/expected/bloom.out
> > b/contrib/bloom/expected/bloom.out index dae12a7..e79456d 100644
> > --- a/contrib/bloom/expected/bloom.out
> > +++ b/contrib/bloom/expected/bloom.out
> > @@ -228,3 +228,6 @@ CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH
> > (length=0);>
> >  ERROR:  value 0 out of bounds for option "length"
> >  CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
> >  ERROR:  value 0 out of bounds for option "col1"
> >
> > +-- 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
> > diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
> > index 4733e1e..0bfc767 100644
> > --- a/contrib/bloom/sql/bloom.sql
> > +++ b/contrib/bloom/sql/bloom.sql
> > @@ -93,3 +93,6 @@ SELECT reloptions FROM pg_class WHERE oid =
> > 'bloomidx'::regclass;>
> >  \set VERBOSITY terse
> >  CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
> >  CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
> >
> > +
> > +-- check post_validate for colN<lengh
> > +CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH
> > (length=10,col1=11);
> > diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
> > index 3a0beaa..a15a10b 100644
> > --- a/contrib/dblink/dblink.c
> > +++ b/contrib/dblink/dblink.c
> > @@ -2005,7 +2005,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 2c2f149..1194747 100644
> > --- a/contrib/file_fdw/file_fdw.c
> > +++ b/contrib/file_fdw/file_fdw.c
> > @@ -195,7 +195,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 5bb1af4..bbd4167 100644
> > --- a/contrib/postgres_fdw/option.c
> > +++ b/contrib/postgres_fdw/option.c
> > @@ -72,7 +72,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 ccc9fa0..5dd52a4 100644
> > --- a/src/backend/access/brin/brin.c
> > +++ b/src/backend/access/brin/brin.c
> > @@ -20,7 +20,6 @@
> >
> >  #include "access/brin_pageops.h"
> >  #include "access/brin_xlog.h"
> >  #include "access/relation.h"
> >
> > -#include "access/reloptions.h"
> >
> >  #include "access/relscan.h"
> >  #include "access/table.h"
> >  #include "access/tableam.h"
> >
> > @@ -40,7 +39,6 @@
> >
> >  #include "utils/memutils.h"
> >  #include "utils/rel.h"
> >
> > -
> >
> >  /*
> >
> >   * We use a BrinBuildState during initial construction of a BRIN index.
> >   * The running state is kept in a BrinMemTuple.
> >
> > @@ -119,7 +117,6 @@ brinhandler(PG_FUNCTION_ARGS)
> >
> >      amroutine->amvacuumcleanup = brinvacuumcleanup;
> >      amroutine->amcanreturn = NULL;
> >      amroutine->amcostestimate = brincostestimate;
> >
> > -    amroutine->amoptions = brinoptions;
> >
> >      amroutine->amproperty = NULL;
> >      amroutine->ambuildphasename = NULL;
> >      amroutine->amvalidate = brinvalidate;
> >
> > @@ -134,6 +131,7 @@ brinhandler(PG_FUNCTION_ARGS)
> >
> >      amroutine->amestimateparallelscan = NULL;
> >      amroutine->aminitparallelscan = NULL;
> >      amroutine->amparallelrescan = NULL;
> >
> > +    amroutine->amreloptspecset = bringetreloptspecset;
> >
> >      PG_RETURN_POINTER(amroutine);
> >
> >  }
> >
> > @@ -963,23 +961,6 @@ brinvacuumcleanup(IndexVacuumInfo *info,
> > IndexBulkDeleteResult *stats)>
> >  }
> >
> >  /*
> >
> > - * reloptions processor for BRIN indexes
> > - */
> > -bytea *
> > -brinoptions(Datum reloptions, bool validate)
> > -{
> > -    static const relopt_parse_elt tab[] = {
> > -        {"pages_per_range", RELOPT_TYPE_INT, offsetof(BrinOptions,
> > pagesPerRange)}, -        {"autosummarize", RELOPT_TYPE_BOOL,
> > offsetof(BrinOptions, autosummarize)} -    };
> > -
> > -    return (bytea *) build_reloptions(reloptions, validate,
> > -
RELOPT_KIND_BRIN,
> > -
sizeof(BrinOptions),
> > -
tab, lengthof(tab));
> > -}
> > -
> > -/*
> >
> >   * SQL-callable function to scan through an index and summarize all
> >   ranges
> >   * that are not currently summarized.
> >   */
> >
> > @@ -1765,3 +1746,32 @@ check_null_keys(BrinValues *bval, ScanKey
> > *nullkeys, int nnullkeys)>
> >      return true;
> >
> >  }
> >
> > +
> > +static options_spec_set *brin_relopt_specset = NULL;
> > +
> > +void *
> > +bringetreloptspecset(void)
> > +{
> > +    if (brin_relopt_specset)
> > +        return brin_relopt_specset;
> > +    brin_relopt_specset = allocateOptionsSpecSet(NULL,
> > +
         sizeof(BrinOptions), 2);
> > +
> > +    optionsSpecSetAddInt(brin_relopt_specset, "pages_per_range",
> > +           "Number of pages that each page range covers in a BRIN
index",
> > +                             NoLock,        /*
since ALTER is not allowed
> > +
     * no lock needed */
> > +                             0,
> > +                             offsetof(BrinOptions,
pagesPerRange),
> > +
BRIN_DEFAULT_PAGES_PER_RANGE,
> > +
BRIN_MIN_PAGES_PER_RANGE,
> > +
BRIN_MAX_PAGES_PER_RANGE);
> > +        optionsSpecSetAddBool(brin_relopt_specset, "autosummarize",
> > +                    "Enables automatic summarization on
this BRIN index",
> > +
AccessExclusiveLock,
> > +                              0,
> > +
offsetof(BrinOptions, autosummarize),
> > +                              false);
> > +    return brin_relopt_specset;
> > +}
> > +
> > diff --git a/src/backend/access/brin/brin_pageops.c
> > b/src/backend/access/brin/brin_pageops.c index df9ffc2..1940b3d 100644
> > --- a/src/backend/access/brin/brin_pageops.c
> > +++ b/src/backend/access/brin/brin_pageops.c
> > @@ -420,6 +420,9 @@ brin_doinsert(Relation idxrel, BlockNumber
> > pagesPerRange,>
> >          freespace = br_page_get_freespace(page);
> >
> >      ItemPointerSet(&tid, blk, off);
> >
> > +
> > +//elog(WARNING, "pages_per_range = %i", pagesPerRange);
> > +
> >
> >      brinSetHeapBlockItemptr(revmapbuf, pagesPerRange, heapBlk, tid);
> >      MarkBufferDirty(revmapbuf);
> >
> > diff --git a/src/backend/access/common/Makefile
> > b/src/backend/access/common/Makefile index b9aff0c..78c9c5a 100644
> > --- a/src/backend/access/common/Makefile
> > +++ b/src/backend/access/common/Makefile
> > @@ -18,6 +18,7 @@ OBJS = \
> >
> >      detoast.o \
> >      heaptuple.o \
> >      indextuple.o \
> >
> > +    options.o \
> >
> >      printsimple.o \
> >      printtup.o \
> >      relation.o \
> >
> > diff --git a/src/backend/access/common/options.c
> > b/src/backend/access/common/options.c new file mode 100644
> > index 0000000..752cddc
> > --- /dev/null
> > +++ b/src/backend/access/common/options.c
> > @@ -0,0 +1,1468 @@
> > +/*-----------------------------------------------------------------------
> > -- + *
> > + * options.c
> > + *      An unifom, context-free API for processing name=value options.
Used
> > + *      to process relation optons (reloptions), attribute options,
opclass
> > + *      options, etc.
> > + *
> > + * Portions Copyright (c) 1996-2021, 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"
> > +
> > +
> > +/*
> > + * OPTIONS SPECIFICATION and OPTION SPECIFICATION SET
> > + *
> > + * Each option is defined via Option Specification object (Option Spec).
> > + * Option Spec should have all information that is needed for processing
> > + * (parsing, validating, converting) of a single option. Implemented via
> > set of + * option_spec_* structures.
> > + *
> > + * A set of Option Specs (Options Spec Set), defines all options
> > available for + * certain object (certain relation kind for example). It
> > is a list of + * Options Specs, plus validation functions that can be
> > used to validate whole + * option set, if needed. Implemenred via
> > options_spec_set structure and set of + * optionsSpecSetAdd* functions
> > that are used for adding Option Specs items to + * a Set.
> > + *
> > + * NOTE: we choose therm "sepcification" instead of "definition" because
> > therm + * "definition" is used for objects that came from lexer. So to
> > avoud confusion + * here we have Option Specifications, and all
> > "definitions" are from lexer. + */
> > +
> > +/*
> > + * OPTION VALUES REPRESENTATIONS
> > + *
> > + * Option values usually came from lexer in form of defList obect, stored
> > in + * pg_catalog as text array, and used when they are stored in memory
> > as + * C-structure. These are different option values representations.
> > Here goes + * brief description of all representations used in the code.
> > + *
> > + * Values
> > + *
> > + * Values are an internal representation that is used while converting
> > + * Values between other representation. Value is called "parsed",
> > + * when Value's value is converted to a proper type and validated, or is
> > called + * "unparsed", when Value's value is stored as raw string that
> > was obtained + * from the source without any cheks. In convertation
> > funcion names first case + * is refered as Values, second case is refered
> > as RawValues. Values is + * implemented as List of option_value
> > C-structures.
> > + *
> > + * defList
> > + *
> > + * Options in form of definition List that comes from lexer. (For
> > reloptions it + * is a part of SQL query that goes after WITH, SET or
> > RESET keywords). Can be + * converted to and from Values using
> > optionsDefListToRawValues and + * optionsTextArrayToRawValues functions.
> > + *
> > + * TEXT[]
> > + *
> > + * Options in form suitable for storig in TEXT[] field in DB. (E.g.
> > reloptions + * are stores in pg_catalog.pg_class table in reloptions
> > field). Can be converted + * to and from Values using
> > optionsValuesToTextArray and optionsTextArrayToRawValues + * functions.
> > + *
> > + * Bytea
> > + *
> > + * Option data stored in C-structure with varlena header in the beginning
> > of the + * structure. This representation is used to pass option values
> > to the core + * postgres. It is fast to read, it can be cached and so on.
> > Bytea rpresentation + * can be obtained from Vales using
> > optionsValuesToBytea function, and can't be + * converted back.
> > + */
> > +
> > +static option_spec_basic *allocateOptionSpec(int type, const char *name,
> > +                         const char *desc, LOCKMODE
lockmode,
> > +                         option_spec_flags flags, int
struct_offset);
> > +
> > +static void parse_one_option(option_value * option, const char *text_str,
> > +                 int text_len, bool validate);
> > +static void *optionsAllocateBytea(options_spec_set * spec_set, List
> > *options); +
> > +
> > +static List *
> > +optionsDefListToRawValues(List *defList, options_parse_mode
> > +                          parse_mode);
> > +static Datum optionsValuesToTextArray(List *options_values);
> > +static List *optionsMergeOptionValues(List *old_options, List
> > *new_options); +static bytea *optionsValuesToBytea(List *options,
> > options_spec_set * spec_set); +List *optionsTextArrayToRawValues(Datum
> > array_datum);
> > +List *optionsParseRawValues(List *raw_values, options_spec_set *
> > spec_set,
> > +                      options_parse_mode mode);
> > +
> > +
> > +/*
> > + * Options spec_set 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_spec_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.
> > +*/
> > +
> > +/*
> > + * allocateOptionsSpecSet
> > + *        Creates new Option Spec Set object: Allocates memory and
initializes
> > + *        structure members.
> > + *
> > + * Spec Set items can be add via allocateOptionSpec and
> > optionSpecSetAddItem functions + * or by calling directly any of
> > optionsSpecSetAdd* function (preferable way) + *
> > + * namespace - Spec Set can be bind to certain namespace (E.g.
> > + * namespace.option=value). Options from other namespaces will be ignored
> > while + * processing. If set to NULL, no namespace will be used at all.
> > + *
> > + * size_of_bytea - size of target structure of Bytea options
> > represenation
> > + *
> > + * num_items_expected - if you know expected number of Spec Set items set
> > it here. + * Set to -1 in other cases. num_items_expected will be used
> > for preallocating memory + * and will trigger error, if you try to add
> > more items than you expected. + */
> > +
> > +options_spec_set *
> > +allocateOptionsSpecSet(const char *namespace, int size_of_bytea, int
> > num_items_expected) +{
> > +    MemoryContext oldcxt;
> > +    options_spec_set *spec_set;
> > +
> > +    oldcxt = MemoryContextSwitchTo(TopMemoryContext);
> > +    spec_set = palloc(sizeof(options_spec_set));
> > +    if (namespace)
> > +    {
> > +        spec_set->namespace = palloc(strlen(namespace) + 1);
> > +        strcpy(spec_set->namespace, namespace);
> > +    }
> > +    else
> > +        spec_set->namespace = NULL;
> > +    if (num_items_expected > 0)
> > +    {
> > +        spec_set->num_allocated = num_items_expected;
> > +        spec_set->forbid_realloc = true;
> > +        spec_set->definitions = palloc(
> > +                 spec_set->num_allocated *
sizeof(option_spec_basic *));
> > +    }
> > +    else
> > +    {
> > +        spec_set->num_allocated = 0;
> > +        spec_set->forbid_realloc = false;
> > +        spec_set->definitions = NULL;
> > +    }
> > +    spec_set->num = 0;
> > +    spec_set->struct_size = size_of_bytea;
> > +    spec_set->postprocess_fun = NULL;
> > +    MemoryContextSwitchTo(oldcxt);
> > +    return spec_set;
> > +}
> > +
> > +/*
> > + * allocateOptionSpec
> > + *        Allocates a new Option Specifiation object of desired type
and
> > + *        initialize the type-independent fields
> > + */
> > +static option_spec_basic *
> > +allocateOptionSpec(int type, const char *name, const char *desc, LOCKMODE
> > lockmode, +                         option_spec_flags
flags, int struct_offset)
> > +{
> > +    MemoryContext oldcxt;
> > +    size_t        size;
> > +    option_spec_basic *newoption;
> > +
> > +    oldcxt = MemoryContextSwitchTo(TopMemoryContext);
> > +
> > +    switch (type)
> > +    {
> > +        case OPTION_TYPE_BOOL:
> > +            size = sizeof(option_spec_bool);
> > +            break;
> > +        case OPTION_TYPE_INT:
> > +            size = sizeof(option_spec_int);
> > +            break;
> > +        case OPTION_TYPE_REAL:
> > +            size = sizeof(option_spec_real);
> > +            break;
> > +        case OPTION_TYPE_ENUM:
> > +            size = sizeof(option_spec_enum);
> > +            break;
> > +        case OPTION_TYPE_STRING:
> > +            size = sizeof(option_spec_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;
> > +}
> > +
> > +/*
> > + * optionSpecSetAddItem
> > + *        Adds pre-created Option Specification objec to the Spec Set
> > + */
> > +static void
> > +optionSpecSetAddItem(option_spec_basic * newoption,
> > +                     options_spec_set * spec_set)
> > +{
> > +    if (spec_set->num >= spec_set->num_allocated)
> > +    {
> > +        MemoryContext oldcxt;
> > +
> > +        Assert(!spec_set->forbid_realloc);
> > +        oldcxt = MemoryContextSwitchTo(TopMemoryContext);
> > +
> > +        if (spec_set->num_allocated == 0)
> > +        {
> > +            spec_set->num_allocated = 8;
> > +            spec_set->definitions = palloc(
> > +                 spec_set->num_allocated *
sizeof(option_spec_basic *));
> > +        }
> > +        else
> > +        {
> > +            spec_set->num_allocated *= 2;
> > +            spec_set->definitions = repalloc(spec_set->definitions,
> > +                 spec_set->num_allocated *
sizeof(option_spec_basic *));
> > +        }
> > +        MemoryContextSwitchTo(oldcxt);
> > +    }
> > +    spec_set->definitions[spec_set->num] = newoption;
> > +    spec_set->num++;
> > +}
> > +
> > +
> > +/*
> > + * optionsSpecSetAddBool
> > + *        Adds boolean Option Specification entry to the Spec Set
> > + */
> > +void
> > +optionsSpecSetAddBool(options_spec_set * spec_set, const char *name,
> > const char *desc, +
LOCKMODE lockmode, option_spec_flags flags,
> > +                          int struct_offset, bool
default_val)
> > +{
> > +    option_spec_bool *spec_set_item;
> > +
> > +    spec_set_item = (option_spec_bool *)
> > +        allocateOptionSpec(OPTION_TYPE_BOOL, name, desc, lockmode,
> > +                                 flags,
struct_offset);
> > +
> > +    spec_set_item->default_val = default_val;
> > +
> > +    optionSpecSetAddItem((option_spec_basic *) spec_set_item, spec_set);
> > +}
> > +
> > +/*
> > + * optionsSpecSetAddInt
> > + *        Adds integer Option Specification entry to the Spec Set
> > + */
> > +void
> > +optionsSpecSetAddInt(options_spec_set * spec_set, const char *name,
> > +          const char *desc, LOCKMODE lockmode, option_spec_flags flags,
> > +                int struct_offset, int default_val, int
min_val, int max_val)
> > +{
> > +    option_spec_int *spec_set_item;
> > +
> > +    spec_set_item = (option_spec_int *)
> > +        allocateOptionSpec(OPTION_TYPE_INT, name, desc, lockmode,
> > +                                 flags,
struct_offset);
> > +
> > +    spec_set_item->default_val = default_val;
> > +    spec_set_item->min = min_val;
> > +    spec_set_item->max = max_val;
> > +
> > +    optionSpecSetAddItem((option_spec_basic *) spec_set_item, spec_set);
> > +}
> > +
> > +/*
> > + * optionsSpecSetAddReal
> > + *        Adds float Option Specification entry to the Spec Set
> > + */
> > +void
> > +optionsSpecSetAddReal(options_spec_set * spec_set, const char *name,
> > const char *desc, +         LOCKMODE lockmode, option_spec_flags
flags, int
> > struct_offset, +                          double
default_val, double min_val, double
> > max_val)
> > +{
> > +    option_spec_real *spec_set_item;
> > +
> > +    spec_set_item = (option_spec_real *)
> > +        allocateOptionSpec(OPTION_TYPE_REAL, name, desc, lockmode,
> > +                                 flags,
struct_offset);
> > +
> > +    spec_set_item->default_val = default_val;
> > +    spec_set_item->min = min_val;
> > +    spec_set_item->max = max_val;
> > +
> > +    optionSpecSetAddItem((option_spec_basic *) spec_set_item, spec_set);
> > +}
> > +
> > +/*
> > + * optionsSpecSetAddEnum
> > + *        Adds enum Option Specification entry to the Spec Set
> > + *
> > + * The members array must have a terminating NULL entry.
> > + *
> > + * The detailmsg is shown when unsupported values are passed, and has
> > this
> > + * form:   "Valid values are \"foo\", \"bar\", and \"bar\"."
> > + *
> > + * The members array and detailmsg are not copied -- caller must ensure
> > that + * they are valid throughout the life of the process.
> > + */
> > +
> > +void
> > +optionsSpecSetAddEnum(options_spec_set * spec_set, const char *name,
> > const char *desc, +        LOCKMODE lockmode, option_spec_flags flags,
int
> > struct_offset,
> > +        opt_enum_elt_def * members, int default_val, const char
*detailmsg)
> > +{
> > +    option_spec_enum *spec_set_item;
> > +
> > +    spec_set_item = (option_spec_enum *)
> > +        allocateOptionSpec(OPTION_TYPE_ENUM, name, desc, lockmode,
> > +                                 flags,
struct_offset);
> > +
> > +    spec_set_item->default_val = default_val;
> > +    spec_set_item->members = members;
> > +    spec_set_item->detailmsg = detailmsg;
> > +
> > +    optionSpecSetAddItem((option_spec_basic *) spec_set_item, spec_set);
> > +}
> > +
> > +/*
> > + * optionsSpecSetAddString
> > + *        Adds string Option Specification entry to the Spec Set
> > + *
> > + * "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
> > +optionsSpecSetAddString(options_spec_set * spec_set, const char *name,
> > const char *desc, +         LOCKMODE lockmode, option_spec_flags
flags, int
> > struct_offset, +                   const char *default_val,
validate_string_option
> > validator) +{
> > +    option_spec_string *spec_set_item;
> > +
> > +    /* make sure the validator/default combination is sane */
> > +    if (validator)
> > +        (validator) (default_val);
> > +
> > +    spec_set_item = (option_spec_string *)
> > +        allocateOptionSpec(OPTION_TYPE_STRING, name, desc, lockmode,
> > +                                 flags,
struct_offset);
> > +    spec_set_item->validate_cb = validator;
> > +
> > +    if (default_val)
> > +        spec_set_item->default_val =
MemoryContextStrdup(TopMemoryContext,
> > +
                default_val);
> > +    else
> > +        spec_set_item->default_val = NULL;
> > +    optionSpecSetAddItem((option_spec_basic *) spec_set_item, spec_set);
> > +}
> > +
> > +
> > +/*
> > + * Options transform functions
> > + */
> > +
> > +/* FIXME this comment should be updated
> > + * 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 spec_set + * 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 spec_set 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 spec_set. + *
> > + * 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
> > spec_set 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 spec_set data, and then use + * it as a usual Datum object,
> > when needed. This Datum should be cached + * somewhere (for example in
> > rel->rd_options for relations) when object that + * has option is loaded
> > from db.
> > + */
> > +
> > +
> > +/* optionsDefListToRawValues
> > + *        Converts option values that came from syntax analyzer
(DefList) into
> > + *        Values List.
> > + *
> > + * No parsing is done here except for checking that RESET syntax is
> > correct + * (syntax analyzer do not see difference between SET and RESET
> > cases, we + * should treat it here manually
> > + */
> > +static List *
> > +optionsDefListToRawValues(List *defList, options_parse_mode parse_mode)
> > +{
> > +    ListCell   *cell;
> > +    List       *result = NIL;
> > +
> > +    foreach(cell, defList)
> > +    {
> > +        option_value *option_dst;
> > +        DefElem    *def = (DefElem *) lfirst(cell);
> > +        char       *value;
> > +
> > +        option_dst = palloc(sizeof(option_value));
> > +
> > +        if (def->defnamespace)
> > +        {
> > +            option_dst->namespace = palloc(strlen(def-
>defnamespace) + 1);
> > +            strcpy(option_dst->namespace, def->defnamespace);
> > +        }
> > +        else
> > +        {
> > +            option_dst->namespace = NULL;
> > +        }
> > +        option_dst->raw_name = palloc(strlen(def->defname) + 1);
> > +        strcpy(option_dst->raw_name, def->defname);
> > +
> > +        if (parse_mode & OPTIONS_PARSE_MODE_FOR_RESET)
> > +        {
> > +            /*
> > +             * If this option came from RESET statement we should
throw error
> > +             * it it brings us name=value data, as syntax
analyzer do not
> > +             * prevent it
> > +             */
> > +            if (def->arg != NULL)
> > +                ereport(ERROR,
> > +
(errcode(ERRCODE_SYNTAX_ERROR),
> > +                    errmsg("RESET must not include values
for parameters")));
> > +
> > +            option_dst->status = OPTION_VALUE_STATUS_FOR_RESET;
> > +        }
> > +        else
> > +        {
> > +            /*
> > +             * For SET statement we should treat (name)
expression as if it is
> > +             * actually (name=true) so do it here manually. In
other cases
> > +             * just use value as we should use it
> > +             */
> > +            option_dst->status = OPTION_VALUE_STATUS_RAW;
> > +            if (def->arg != NULL)
> > +                value = defGetString(def);
> > +            else
> > +                value = "true";
> > +            option_dst->raw_value = palloc(strlen(value) + 1);
> > +            strcpy(option_dst->raw_value, value);
> > +        }
> > +
> > +        result = lappend(result, option_dst);
> > +    }
> > +    return result;
> > +}
> > +
> > +/*
> > + * optionsValuesToTextArray
> > + *        Converts List of option_values into TextArray
> > + *
> > + *    Convertation is made to put options into database (e.g. in
> > + *    pg_class.reloptions for all relation options)
> > + */
> > +
> > +Datum
> > +optionsValuesToTextArray(List *options_values)
> > +{
> > +    ArrayBuildState *astate = NULL;
> > +    ListCell   *cell;
> > +    Datum        result;
> > +
> > +    foreach(cell, options_values)
> > +    {
> > +        option_value *option = (option_value *) lfirst(cell);
> > +        const char *name;
> > +        char       *value;
> > +        text       *t;
> > +        int            len;
> > +
> > +        /*
> > +         * Raw value were not cleared while parsing, so instead of
converting
> > +         * it back, just use it to store value as text
> > +         */
> > +        value = option->raw_value;
> > +
> > +        Assert(option->status != OPTION_VALUE_STATUS_EMPTY);
> > +
> > +        /*
> > +         * Name will be taken from option definition, if option were
parsed or
> > +         * from raw_name if option were not parsed for some reason
> > +         */
> > +        if (option->status == OPTION_VALUE_STATUS_PARSED)
> > +            name = option->gen->name;
> > +        else
> > +            name = option->raw_name;
> > +
> > +        /*
> > +         * Now build "name=value" string and append it to the array
> > +         */
> > +        len = VARHDRSZ + strlen(name) + strlen(value) + 1;
> > +        t = (text *) palloc(len + 1);
> > +        SET_VARSIZE(t, len);
> > +        sprintf(VARDATA(t), "%s=%s", name, value);
> > +        astate = accumArrayResult(astate, PointerGetDatum(t), false,
> > +                                  TEXTOID,
CurrentMemoryContext);
> > +    }
> > +    if (astate)
> > +        result = makeArrayResult(astate, CurrentMemoryContext);
> > +    else
> > +        result = (Datum) 0;
> > +
> > +    return result;
> > +}
> > +
> > +/*
> > + * optionsTextArrayToRawValues
> > + *        Converts options from TextArray format into RawValues list.
> > + *
> > + *    This function is used to convert options data that comes from
database
> > to + *    List of option_values, for further parsing, and, in the case of
> > ALTER + *    command, for merging with new option values.
> > + */
> > +List *
> > +optionsTextArrayToRawValues(Datum array_datum)
> > +{
> > +    List       *result = NIL;
> > +
> > +    if (PointerIsValid(DatumGetPointer(array_datum)))
> > +    {
> > +        ArrayType  *array = DatumGetArrayTypeP(array_datum);
> > +        Datum       *options;
> > +        int            noptions;
> > +        int            i;
> > +
> > +        deconstruct_array(array, TEXTOID, -1, false, 'i',
> > +                          &options, NULL, &noptions);
> > +
> > +        for (i = 0; i < noptions; i++)
> > +        {
> > +            option_value *option_dst;
> > +            char       *text_str = VARDATA(options[i]);
> > +            int            text_len =
VARSIZE(options[i]) - VARHDRSZ;
> > +            int            i;
> > +            int            name_len = -1;
> > +            char       *name;
> > +            int            raw_value_len;
> > +            char       *raw_value;
> > +
> > +            /*
> > +             * Find position of '=' sign and treat id as a
separator between
> > +             * name and value in "name=value" item
> > +             */
> > +            for (i = 0; i < text_len; i = i + pg_mblen(text_str))
> > +            {
> > +                if (text_str[i] == '=')
> > +                {
> > +                    name_len = i;
> > +                    break;
> > +                }
> > +            }
> > +            Assert(name_len >= 1);        /* Just in case
*/
> > +
> > +            raw_value_len = text_len - name_len - 1;
> > +
> > +            /*
> > +             * Copy name from src
> > +             */
> > +            name = palloc(name_len + 1);
> > +            memcpy(name, text_str, name_len);
> > +            name[name_len] = '\0';
> > +
> > +            /*
> > +             * Copy value from src
> > +             */
> > +            raw_value = palloc(raw_value_len + 1);
> > +            memcpy(raw_value, text_str + name_len + 1,
raw_value_len);
> > +            raw_value[raw_value_len] = '\0';
> > +
> > +            /*
> > +             * Create new option_value item
> > +             */
> > +            option_dst = palloc(sizeof(option_value));
> > +            option_dst->status = OPTION_VALUE_STATUS_RAW;
> > +            option_dst->raw_name = name;
> > +            option_dst->raw_value = raw_value;
> > +            option_dst->namespace = NULL;
> > +
> > +            result = lappend(result, option_dst);
> > +        }
> > +    }
> > +    return result;
> > +}
> > +
> > +/*
> > + * optionsMergeOptionValues
> > + *        Merges two lists of option_values into one list
> > + *
> > + * This function is used to merge two Values list into one. It is used
> > for all + * kinds of ALTER commands when existing options are
> > merged|replaced with new + * options list. This function also process
> > RESET variant of ALTER command. It + * merges two lists as usual, and
> > then removes all items with RESET flag on. + *
> > + * Both incoming lists will be destroyed while merging
> > + */
> > +static List *
> > +optionsMergeOptionValues(List *old_options, List *new_options)
> > +{
> > +    List       *result = NIL;
> > +    ListCell   *old_cell;
> > +    ListCell   *new_cell;
> > +
> > +    /*
> > +     * First add to result all old options that are not mentioned in new
> > list
> > +     */
> > +    foreach(old_cell, old_options)
> > +    {
> > +        bool        found;
> > +        const char *old_name;
> > +        option_value *old_option;
> > +
> > +        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;
> > +
> > +        /*
> > +         * Looking for a new option with same name
> > +         */
> > +        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 (strcmp(new_name, old_name) == 0)
> > +            {
> > +                found = true;
> > +                break;
> > +            }
> > +        }
> > +        if (!found)
> > +            result = lappend(result, old_option);
> > +    }
> > +    /*
> > +     * Now add all to result all new options that are not designated for
> > reset +     */
> > +    foreach(new_cell, new_options)
> > +    {
> > +        option_value *new_option;
> > +        new_option = (option_value *) lfirst(new_cell);
> > +
> > +        if(new_option->status != OPTION_VALUE_STATUS_FOR_RESET)
> > +            result = lappend(result, new_option);
> > +    }
> > +    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 (strcmp(def->defnamespace,
> > +
allowed_namespaces[i]) == 0)
> > +                {
> > +                    found = true;
> > +                    break;
> > +                }
> > +                i++;
> > +            }
> > +            if (!found)
> > +                ereport(ERROR,
> > +
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > +                         errmsg("unrecognized
parameter namespace \"%s\"",
> > +                                def-
>defnamespace)));
> > +        }
> > +    }
> > +}
> > +
> > +/*
> > + * optionsDefListFilterNamespaces
> > + *        Iterates over DefList, choose items with specified namespace
and adds
> > + *        them to a result List
> > + *
> > + * This function does not destroy source DefList but does not create
> > copies + * of List nodes.
> > + * It is actually used only for tables, in order to split toast and heap
> > + * reloptions, so each one can be stored in on it's own pg_class record
> > + */
> > +List *
> > +optionsDefListFilterNamespaces(List *defList, const char *namespace)
> > +{
> > +    ListCell   *cell;
> > +    List       *result = NIL;
> > +
> > +    foreach(cell, defList)
> > +    {
> > +        DefElem    *def = (DefElem *) lfirst(cell);
> > +
> > +        if ((!namespace && !def->defnamespace) ||
> > +            (namespace && def->defnamespace &&
> > +             strcmp(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;
> > +}
> > +
> > +/* FIXME write comment here */
> > +
> > +Datum
> > +optionsDefListToTextArray(List *defList)
> > +{
> > +    ListCell   *cell;
> > +    Datum        result;
> > +    ArrayBuildState *astate = NULL;
> > +
> > +    foreach(cell, defList)
> > +    {
> > +        DefElem       *def = (DefElem *) lfirst(cell);
> > +        const char *name = def->defname;
> > +        const char *value;
> > +        text       *t;
> > +        int            len;
> > +
> > +        if (def->arg != NULL)
> > +            value = defGetString(def);
> > +        else
> > +            value = "true";
> > +
> > +        if (def->defnamespace)
> > +        {
> > +            Assert(false); /* Should not get here */
> > +            /* This function is used for backward compatibility
in the place were
> > namespases are not allowed */ +            return (Datum) 0;
> > +        }
> > +        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;
> > +}
> > +
> > +
> > +/*
> > + * 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_spec_set 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_spec_set 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_spec_set * spec_set,
> > +                      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) * spec_set->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 && (!spec_set->namespace ||
> > +                  strcmp(spec_set->namespace, option-
>namespace) != 0))
> > +        {
> > +            ereport(ERROR,
> > +
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > +                     errmsg("unrecognized parameter
namespace \"%s\"",
> > +                            option->namespace)));
> > +        }
> > +
> > +        for (i = 0; i < spec_set->num; i++)
> > +        {
> > +            option_spec_basic *definition = spec_set-
>definitions[i];
> > +
> > +            if (strcmp(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
spec_set. 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)));
> > +            } else
> > +            {
> > +                /* RESET is always in non-validating mode,
unkown names should
> > +                 * be ignored. This is traditional behaviour
of postgres/
> > +                 * FIXME may be it should be changed someday
> > +                 */
> > +                if (option->status ==
OPTION_VALUE_STATUS_FOR_RESET)
> > +                {
> > +                    skip = true;
> > +                }
> > +            }
> > +            /*
> > +             * In other cases, if we are parsing not in validate
mode, then
> > +             * we should keep unknown node, because non-validate
mode is for
> > +             * data that is already in the DB and should not be
changed after
> > +             * altering another entries
> > +             */
> > +        }
> > +        if (!skip)
> > +            result = lappend(result, option);
> > +    }
> > +    return result;
> > +}
> > +
> > +/*
> > + * parse_one_option
> > + *
> > + *        Subroutine for optionsParseRawValues, to parse and validate
a
> > + *        single option's value
> > + */
> > +static void
> > +parse_one_option(option_value * option, const char *text_str, int
> > text_len, +                 bool validate)
> > +{
> > +    char       *value;
> > +    bool        parsed;
> > +
> > +    value = option->raw_value;
> > +
> > +    switch (option->gen->type)
> > +    {
> > +        case OPTION_TYPE_BOOL:
> > +            {
> > +                parsed = parse_bool(value, &option-
>values.bool_val);
> > +                if (validate && !parsed)
> > +                    ereport(ERROR,
> > +
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > +                        errmsg("invalid value for
boolean option \"%s\": %s",
> > +                               option->gen->name,
value)));
> > +            }
> > +            break;
> > +        case OPTION_TYPE_INT:
> > +            {
> > +                option_spec_int *optint =
> > +                (option_spec_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_spec_real *optreal =
> > +                (option_spec_real *) option->gen;
> > +
> > +                parsed = parse_real(value, &option-
>values.real_val, 0, NULL);
> > +                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_spec_enum *optenum =
> > +
(option_spec_enum *) option->gen;
> > +                opt_enum_elt_def *elt;
> > +                parsed = false;
> > +                for (elt = optenum->members; elt->string_val;
elt++)
> > +                {
> > +                    if (strcmp(value, elt->string_val) ==
0)
> > +                    {
> > +                        option->values.enum_val =
elt->symbol_val;
> > +                        parsed = true;
> > +                        break;
> > +                    }
> > +                }
> > +                if (!parsed)
> > +                {
> > +                    ereport(ERROR,
> > +
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > +                             errmsg("invalid
value for enum option \"%s\": %s",
> > +
option->gen->name, value),
> > +                             optenum->detailmsg ?
> > +
errdetail_internal("%s", _(optenum->detailmsg)) : 0));
> > +                }
> > +            }
> > +            break;
> > +        case OPTION_TYPE_STRING:
> > +            {
> > +                option_spec_string *optstring =
> > +                (option_spec_string *) option->gen;
> > +
> > +                option->values.string_val = value;
> > +                if (validate && optstring->validate_cb)
> > +                    (optstring->validate_cb) (value);
> > +                parsed = true;
> > +            }
> > +            break;
> > +        default:
> > +            elog(ERROR, "unsupported reloption type %d", option-
>gen->type);
> > +            parsed = true;        /* quiet compiler */
> > +            break;
> > +    }
> > +
> > +    if (parsed)
> > +        option->status = OPTION_VALUE_STATUS_PARSED;
> > +
> > +}
> > +
> > +/*
> > + * optionsAllocateBytea
> > + *        Allocates memory for bytea options representation
> > + *
> > + * Function allocates memory for byrea structure of an option, plus adds
> > space + * for values of string options. We should keep all data including
> > string + * values in the same memory chunk, because Cache code copies
> > bytea option + * data from one MemoryConext to another without knowing
> > about it's internal + * structure, so it would not be able to copy string
> > values if they are outside + * of bytea memory chunk.
> > + */
> > +static void *
> > +optionsAllocateBytea(options_spec_set * spec_set, List *options)
> > +{
> > +    Size        size;
> > +    int            i;
> > +    ListCell   *cell;
> > +    int            length;
> > +    void       *res;
> > +
> > +    size = spec_set->struct_size;
> > +
> > +    /* Calculate size needed to store all string values for this option
*/
> > +    for (i = 0; i < spec_set->num; i++)
> > +    {
> > +        option_spec_basic *definition = spec_set->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
spec_set
> > +         * entry
> > +         */
> > +        foreach(cell, options)
> > +        {
> > +            option = (option_value *) lfirst(cell);
> > +            if (option->status == OPTION_VALUE_STATUS_PARSED &&
> > +                strcmp(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_spec_string *) definition)->default_val)
> > +            length = strlen(
> > +                 ((option_spec_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_spec_set: each spec_set item
> > + * has offset value, and option value in binary mode is written to the
> > + * structure with that offset.
> > + *
> > + * More special case is string values. Memory for bytea structure is
> > allocated + * by optionsAllocateBytea which adds some more space for
> > string values to + * the size of original structure. All string values
> > are copied there and + * inside the bytea structure an offset to that
> > value is kept.
> > + *
> > + */
> > +static bytea *
> > +optionsValuesToBytea(List *options, options_spec_set * spec_set)
> > +{
> > +    char       *data;
> > +    char       *string_values_buffer;
> > +    int            i;
> > +
> > +    data = optionsAllocateBytea(spec_set, options);
> > +
> > +    /* place for string data starts right after original structure */
> > +    string_values_buffer = data + spec_set->struct_size;
> > +
> > +    for (i = 0; i < spec_set->num; i++)
> > +    {
> > +        option_value *found = NULL;
> > +        ListCell   *cell;
> > +        char       *item_pos;
> > +        option_spec_basic *definition = spec_set->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 (strcmp(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_spec_bool *) definition)-
>default_val;
> > +                break;
> > +            case OPTION_TYPE_INT:
> > +                *(int *) item_pos = found ?
> > +                    found->values.int_val :
> > +                    ((option_spec_int *) definition)-
>default_val;
> > +                break;
> > +            case OPTION_TYPE_REAL:
> > +                *(double *) item_pos = found ?
> > +                    found->values.real_val :
> > +                    ((option_spec_real *) definition)-
>default_val;
> > +                break;
> > +            case OPTION_TYPE_ENUM:
> > +                *(int *) item_pos = found ?
> > +                    found->values.enum_val :
> > +                    ((option_spec_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_spec_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_spec_set * spec_set, 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);
> > +    if (! (parse_mode & OPTIONS_PARSE_MODE_FOR_RESET))
> > +    {
> > +        /* FIXME: postgres usual behaviour vas not to vaidate names
that
> > +         * came from RESET command. Once this behavious should be
changed,
> > +         * I guess. But for now we keep it as it was.
> > +         */
> > +        parse_mode|= OPTIONS_PARSE_MODE_VALIDATE;
> > +    }
> > +    new_values = optionsParseRawValues(new_values, spec_set, parse_mode);
> > +
> > +    /*
> > +     * 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,
spec_set, 0);
> > +    }
> > +    else
> > +    {
> > +        merged_values = new_values;
> > +    }
> > +
> > +    /*
> > +     * If we have postprocess_fun function defined in spec_set, 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 (spec_set->postprocess_fun)
> > +    {
> > +        bytea       *data = optionsValuesToBytea(merged_values,
spec_set);
> > +
> > +        spec_set->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_spec_set * spec_set, Datum data, bool
> > validate) +{
> > +    List       *values;
> > +    bytea       *options;
> > +
> > +    values = optionsTextArrayToRawValues(data);
> > +    values = optionsParseRawValues(values, spec_set,
> > +                                validate ?
OPTIONS_PARSE_MODE_VALIDATE : 0);
> > +    options = optionsValuesToBytea(values, spec_set);
> > +
> > +    if (spec_set->postprocess_fun)
> > +    {
> > +        spec_set->postprocess_fun(options, false);
> > +    }
> > +    return options;
> > +}
> > diff --git a/src/backend/access/common/relation.c
> > b/src/backend/access/common/relation.c index 632d13c..49ad197 100644
> > --- a/src/backend/access/common/relation.c
> > +++ b/src/backend/access/common/relation.c
> > @@ -65,9 +65,13 @@ relation_open(Oid relationId, LOCKMODE lockmode)
> >
> >       * If we didn't get the lock ourselves, assert that caller holds
one,
> >       * except in bootstrap mode where no locks are used.
> >       */
> >
> > -    Assert(lockmode != NoLock ||
> > -           IsBootstrapProcessingMode() ||
> > -           CheckRelationLockedByMe(r, AccessShareLock, true));
> > +
> > +// FIXME We need NoLock mode to get AM data when choosing Lock for
> > +// attoptions is changed. See ProcessUtilitySlow problems comes from
> > there
> > +// This is a dirty hack, we need better solution for this case;
> > +//    Assert(lockmode != NoLock ||
> > +//           IsBootstrapProcessingMode() ||
> > +//           CheckRelationLockedByMe(r, AccessShareLock, true));
> >
> >      /* Make note that we've accessed a temporary relation */
> >      if (RelationUsesLocalBuffers(r))
> >
> > diff --git a/src/backend/access/common/reloptions.c
> > b/src/backend/access/common/reloptions.c index b5602f5..29ab98a 100644
> > --- a/src/backend/access/common/reloptions.c
> > +++ b/src/backend/access/common/reloptions.c
> > @@ -1,7 +1,7 @@
> >
> >  /*-----------------------------------------------------------------------
> >  --
> >
> >   *
> >   * reloptions.c
> >
> > - *      Core support for relation options (pg_class.reloptions)
> > + *      Support for relation options (pg_class.reloptions)
> >
> >   *
> >   * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
> >   * Portions Copyright (c) 1994, Regents of the University of California
> >
> > @@ -17,13 +17,10 @@
> >
> >  #include <float.h>
> >
> > -#include "access/gist_private.h"
> > -#include "access/hash.h"
> >
> >  #include "access/heaptoast.h"
> >  #include "access/htup_details.h"
> >
> > -#include "access/nbtree.h"
> >
> >  #include "access/reloptions.h"
> >
> > -#include "access/spgist_private.h"
> > +#include "access/options.h"
> >
> >  #include "catalog/pg_type.h"
> >  #include "commands/defrem.h"
> >  #include "commands/tablespace.h"
> >
> > @@ -36,6 +33,7 @@
> >
> >  #include "utils/guc.h"
> >  #include "utils/memutils.h"
> >  #include "utils/rel.h"
> >
> > +#include "storage/bufmgr.h"
> >
> >  /*
> >
> >   * Contents of pg_class.reloptions
> >
> > @@ -93,380 +91,8 @@
> >
> >   * value has no effect until the next VACUUM, so no need for stronger
> >   lock.
> >   */
> >
> > -static relopt_bool boolRelOpts[] =
> > -{
> > -    {
> > -        {
> > -            "autosummarize",
> > -            "Enables automatic summarization on this BRIN
index",
> > -            RELOPT_KIND_BRIN,
> > -            AccessExclusiveLock
> > -        },
> > -        false
> > -    },
> > -    {
> > -        {
> > -            "autovacuum_enabled",
> > -            "Enables autovacuum in this relation",
> > -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > -            ShareUpdateExclusiveLock
> > -        },
> > -        true
> > -    },
> > -    {
> > -        {
> > -            "user_catalog_table",
> > -            "Declare a table as an additional catalog table,
e.g. for the purpose
> > of logical replication", -            RELOPT_KIND_HEAP,
> > -            AccessExclusiveLock
> > -        },
> > -        false
> > -    },
> > -    {
> > -        {
> > -            "fastupdate",
> > -            "Enables \"fast update\" feature for this GIN
index",
> > -            RELOPT_KIND_GIN,
> > -            AccessExclusiveLock
> > -        },
> > -        true
> > -    },
> > -    {
> > -        {
> > -            "security_barrier",
> > -            "View acts as a row security barrier",
> > -            RELOPT_KIND_VIEW,
> > -            AccessExclusiveLock
> > -        },
> > -        false
> > -    },
> > -    {
> > -        {
> > -            "vacuum_truncate",
> > -            "Enables vacuum to truncate empty pages at the end
of this table",
> > -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > -            ShareUpdateExclusiveLock
> > -        },
> > -        true
> > -    },
> > -    {
> > -        {
> > -            "deduplicate_items",
> > -            "Enables \"deduplicate items\" feature for this
btree index",
> > -            RELOPT_KIND_BTREE,
> > -            ShareUpdateExclusiveLock    /* since it applies only
to later
> > -
 * inserts */
> > -        },
> > -        true
> > -    },
> > -    /* 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_vacuum_insert_threshold",
> > -            "Minimum number of tuple inserts prior to vacuum, or
-1 to disable
> > insert vacuums", -            RELOPT_KIND_HEAP |
RELOPT_KIND_TOAST,
> > -            ShareUpdateExclusiveLock
> > -        },
> > -        -2, -1, 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_limit",
> > -            "Vacuum cost amount available before napping, for
autovacuum",
> > -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > -            ShareUpdateExclusiveLock
> > -        },
> > -        -1, 1, 10000
> > -    },
> > -    {
> > -        {
> > -            "autovacuum_freeze_min_age",
> > -            "Minimum age at which VACUUM should freeze a table
row, for
> > autovacuum", -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > -            ShareUpdateExclusiveLock
> > -        },
> > -        -1, 0, 1000000000
> > -    },
> > -    {
> > -        {
> > -            "autovacuum_multixact_freeze_min_age",
> > -            "Minimum multixact age at which VACUUM should freeze
a row
> > multixact's, for autovacuum", -            RELOPT_KIND_HEAP |
RELOPT_KIND_TOAST,
> > -            ShareUpdateExclusiveLock
> > -        },
> > -        -1, 0, 1000000000
> > -    },
> > -    {
> > -        {
> > -            "autovacuum_freeze_max_age",
> > -            "Age at which to autovacuum a table to prevent
transaction ID
> > wraparound", -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > -            ShareUpdateExclusiveLock
> > -        },
> > -        -1, 100000, 2000000000
> > -    },
> > -    {
> > -        {
> > -            "autovacuum_multixact_freeze_max_age",
> > -            "Multixact age at which to autovacuum a table to
prevent multixact
> > wraparound", -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > -            ShareUpdateExclusiveLock
> > -        },
> > -        -1, 10000, 2000000000
> > -    },
> > -    {
> > -        {
> > -            "autovacuum_freeze_table_age",
> > -            "Age at which VACUUM should perform a full table
sweep to freeze row
> > versions", -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > -            ShareUpdateExclusiveLock
> > -        }, -1, 0, 2000000000
> > -    },
> > -    {
> > -        {
> > -            "autovacuum_multixact_freeze_table_age",
> > -            "Age of multixact at which VACUUM should perform a
full table sweep to
> > freeze row versions", -            RELOPT_KIND_HEAP |
RELOPT_KIND_TOAST,
> > -            ShareUpdateExclusiveLock
> > -        }, -1, 0, 2000000000
> > -    },
> > -    {
> > -        {
> > -            "log_autovacuum_min_duration",
> > -            "Sets the minimum execution time above which
autovacuum actions will
> > be logged", -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > -            ShareUpdateExclusiveLock
> > -        },
> > -        -1, -1, INT_MAX
> > -    },
> > -    {
> > -        {
> > -            "toast_tuple_target",
> > -            "Sets the target tuple length at which external
columns will be
> > toasted", -            RELOPT_KIND_HEAP,
> > -            ShareUpdateExclusiveLock
> > -        },
> > -        TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
> > -    },
> > -    {
> > -        {
> > -            "pages_per_range",
> > -            "Number of pages that each page range covers in a
BRIN index",
> > -            RELOPT_KIND_BRIN,
> > -            AccessExclusiveLock
> > -        }, 128, 1, 131072
> > -    },
> > -    {
> > -        {
> > -            "gin_pending_list_limit",
> > -            "Maximum size of the pending list for this GIN
index, in kilobytes.",
> > -            RELOPT_KIND_GIN,
> > -            AccessExclusiveLock
> > -        },
> > -        -1, 64, MAX_KILOBYTES
> > -    },
> > -    {
> > -        {
> > -            "effective_io_concurrency",
> > -            "Number of simultaneous requests that can be handled
efficiently by
> > the disk subsystem.", -            RELOPT_KIND_TABLESPACE,
> > -            ShareUpdateExclusiveLock
> > -        },
> > -#ifdef USE_PREFETCH
> > -        -1, 0, MAX_IO_CONCURRENCY
> > -#else
> > -        0, 0, 0
> > -#endif
> > -    },
> > -    {
> > -        {
> > -            "maintenance_io_concurrency",
> > -            "Number of simultaneous requests that can be handled
efficiently by
> > the disk subsystem for maintenance work.", -
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_cost_delay",
> > -            "Vacuum cost delay in milliseconds, for autovacuum",
> > -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > -            ShareUpdateExclusiveLock
> > -        },
> > -        -1, 0.0, 100.0
> > -    },
> > -    {
> > -        {
> > -            "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_vacuum_insert_scale_factor",
> > -            "Number of tuple inserts 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
> > -    },
> > -    {
> > -        {
> > -            "vacuum_cleanup_index_scale_factor",
> > -            "Deprecated B-Tree parameter.",
> > -            RELOPT_KIND_BTREE,
> > -            ShareUpdateExclusiveLock
> > -        },
> > -        -1, 0.0, 1e10
> > -    },
> > -    /* list terminator */
> > -    {{NULL}}
> > -};
> > -
> >
> >  /* values from StdRdOptIndexCleanup */
> >
> > -relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
> > +opt_enum_elt_def StdRdOptIndexCleanupValues[] =
> >
> >  {
> >
> >      {"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
> >      {"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
> >
> > @@ -480,17 +106,8 @@ relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
> >
> >      {(const char *) NULL}        /* list terminator */
> >
> >  };
> >
> > -/* values from GistOptBufferingMode */
> > -relopt_enum_elt_def gistBufferingOptValues[] =
> > -{
> > -    {"auto", GIST_OPTION_BUFFERING_AUTO},
> > -    {"on", GIST_OPTION_BUFFERING_ON},
> > -    {"off", GIST_OPTION_BUFFERING_OFF},
> > -    {(const char *) NULL}        /* list terminator */
> > -};
> > -
> >
> >  /* values from ViewOptCheckOption */
> >
> > -relopt_enum_elt_def viewCheckOptValues[] =
> > +opt_enum_elt_def viewCheckOptValues[] =
> >
> >  {
> >
> >      /* no value for NOT_SET */
> >      {"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
> >
> > @@ -498,61 +115,8 @@ relopt_enum_elt_def viewCheckOptValues[] =
> >
> >      {(const char *) NULL}        /* list terminator */
> >
> >  };
> >
> > -static relopt_enum enumRelOpts[] =
> > -{
> > -    {
> > -        {
> > -            "vacuum_index_cleanup",
> > -            "Controls index vacuuming and index cleanup",
> > -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > -            ShareUpdateExclusiveLock
> > -        },
> > -        StdRdOptIndexCleanupValues,
> > -        STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO,
> > -        gettext_noop("Valid values are \"on\", \"off\", and
\"auto\".")
> > -    },
> > -    {
> > -        {
> > -            "buffering",
> > -            "Enables buffering build for this GiST index",
> > -            RELOPT_KIND_GIST,
> > -            AccessExclusiveLock
> > -        },
> > -        gistBufferingOptValues,
> > -        GIST_OPTION_BUFFERING_AUTO,
> > -        gettext_noop("Valid values are \"on\", \"off\", and
\"auto\".")
> > -    },
> > -    {
> > -        {
> > -            "check_option",
> > -            "View has WITH CHECK OPTION defined (local or
cascaded).",
> > -            RELOPT_KIND_VIEW,
> > -            AccessExclusiveLock
> > -        },
> > -        viewCheckOptValues,
> > -        VIEW_OPTION_CHECK_OPTION_NOT_SET,
> > -        gettext_noop("Valid values are \"local\" and \"cascaded\".")
> > -    },
> > -    /* list terminator */
> > -    {{NULL}}
> > -};
> > -
> > -static relopt_string stringRelOpts[] =
> > -{
> > -    /* 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);
> > +options_spec_set *get_stdrd_relopt_spec_set(relopt_kind kind);
> >
> >  /*
> >
> >   * Get the length of a string reloption (either default or the
> >   user-defined
> >
> > @@ -563,160 +127,6 @@ static void parse_one_reloption(relopt_value
> > *option, char *text_str,>
> >      ((option).isset ? strlen((option).values.string_val) : \
> >
> >       ((relopt_string *) (option).gen)->default_len)
> >
> > -/*
> > - * 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; enumRelOpts[i].gen.name; i++)
> > -    {
> > -        Assert(DoLockModesConflict(enumRelOpts[i].gen.lockmode,
> > -
enumRelOpts[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; enumRelOpts[i].gen.name; i++)
> > -    {
> > -        relOpts[j] = &enumRelOpts[i].gen;
> > -        relOpts[j]->type = RELOPT_TYPE_ENUM;
> > -        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;
> > -}
> >
> >  /*
> >
> >   * init_local_reloptions
> >
> > @@ -729,6 +139,7 @@ init_local_reloptions(local_relopts *opts, Size
> > relopt_struct_size)>
> >      opts->options = NIL;
> >      opts->validators = NIL;
> >      opts->relopt_struct_size = relopt_struct_size;
> >
> > +    opts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, 0);
> >
> >  }
> >
> >  /*
> >
> > @@ -743,112 +154,6 @@ register_reloptions_validator(local_relopts *opts,
> > relopts_validator validator)>
> >  }
> >
> >  /*
> >
> > - * add_local_reloption
> > - *        Add an already-created custom reloption to the local list.
> > - */
> > -static void
> > -add_local_reloption(local_relopts *relopts, relopt_gen *newoption, int
> > offset) -{
> > -    local_relopt *opt = palloc(sizeof(*opt));
> > -
> > -    Assert(offset < relopts->relopt_struct_size);
> > -
> > -    opt->option = newoption;
> > -    opt->offset = offset;
> > -
> > -    relopts->options = lappend(relopts->options, opt);
> > -}
> > -
> > -/*
> > - * allocate_reloption
> > - *        Allocate a new reloption and initialize the type-agnostic
fields
> > - *        (for types other than string)
> > - */
> > -static relopt_gen *
> > -allocate_reloption(bits32 kinds, int type, const char *name, const char
> > *desc, -                   LOCKMODE lockmode)
> > -{
> > -    MemoryContext oldcxt;
> > -    size_t        size;
> > -    relopt_gen *newoption;
> > -
> > -    if (kinds != RELOPT_KIND_LOCAL)
> > -        oldcxt = MemoryContextSwitchTo(TopMemoryContext);
> > -    else
> > -        oldcxt = NULL;
> > -
> > -    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_ENUM:
> > -            size = sizeof(relopt_enum);
> > -            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;
> > -    newoption->lockmode = lockmode;
> > -
> > -    if (oldcxt != NULL)
> > -        MemoryContextSwitchTo(oldcxt);
> > -
> > -    return newoption;
> > -}
> > -
> > -/*
> > - * init_bool_reloption
> > - *        Allocate and initialize a new boolean reloption
> > - */
> > -static relopt_bool *
> > -init_bool_reloption(bits32 kinds, const char *name, const char *desc,
> > -                    bool default_val, LOCKMODE lockmode)
> > -{
> > -    relopt_bool *newoption;
> > -
> > -    newoption = (relopt_bool *) allocate_reloption(kinds,
RELOPT_TYPE_BOOL,
> > -
           name, desc, lockmode);
> > -    newoption->default_val = default_val;
> > -
> > -    return newoption;
> > -}
> > -
> > -/*
> > - * add_bool_reloption
> > - *        Add a new boolean reloption
> > - */
> > -void
> > -add_bool_reloption(bits32 kinds, const char *name, const char *desc,
> > -                   bool default_val, LOCKMODE lockmode)
> > -{
> > -    relopt_bool *newoption = init_bool_reloption(kinds, name, desc,
> > -
         default_val, lockmode);
> > -
> > -    add_reloption((relopt_gen *) newoption);
> > -}
> > -
> > -/*
> >
> >   * add_local_bool_reloption
> >   *        Add a new boolean local reloption
> >   *
> >
> > @@ -858,47 +163,8 @@ void
> >
> >  add_local_bool_reloption(local_relopts *relopts, const char *name,
> >
> >                           const char *desc, bool
default_val, int offset)
> >
> >  {
> >
> > -    relopt_bool *newoption = init_bool_reloption(RELOPT_KIND_LOCAL,
> > -
         name, desc,
> > -
         default_val, 0);
> > -
> > -    add_local_reloption(relopts, (relopt_gen *) newoption, offset);
> > -}
> > -
> > -
> > -/*
> > - * init_real_reloption
> > - *        Allocate and initialize a new integer reloption
> > - */
> > -static relopt_int *
> > -init_int_reloption(bits32 kinds, const char *name, const char *desc,
> > -                   int default_val, int min_val, int
max_val,
> > -                   LOCKMODE lockmode)
> > -{
> > -    relopt_int *newoption;
> > -
> > -    newoption = (relopt_int *) allocate_reloption(kinds,
RELOPT_TYPE_INT,
> > -
          name, desc, lockmode);
> > -    newoption->default_val = default_val;
> > -    newoption->min = min_val;
> > -    newoption->max = max_val;
> > -
> > -    return newoption;
> > -}
> > -
> > -/*
> > - * add_int_reloption
> > - *        Add a new integer reloption
> > - */
> > -void
> > -add_int_reloption(bits32 kinds, const char *name, const char *desc, int
> > default_val, -                  int min_val, int max_val,
LOCKMODE lockmode)
> > -{
> > -    relopt_int *newoption = init_int_reloption(kinds, name, desc,
> > -
       default_val, min_val,
> > -
       max_val, lockmode);
> > -
> > -    add_reloption((relopt_gen *) newoption);
> > +    optionsSpecSetAddBool(relopts->spec_set, name, desc, NoLock, 0,
offset,
> > +
                        default_val);
> >
> >  }
> >
> >  /*
> >
> > @@ -912,47 +178,8 @@ add_local_int_reloption(local_relopts *relopts, const
> > char *name,>
> >                          const char *desc, int
default_val, int min_val,
> >                          int max_val, int offset)
> >
> >  {
> >
> > -    relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
> > -
       name, desc, default_val,
> > -
       min_val, max_val, 0);
> > -
> > -    add_local_reloption(relopts, (relopt_gen *) newoption, offset);
> > -}
> > -
> > -/*
> > - * init_real_reloption
> > - *        Allocate and initialize a new real reloption
> > - */
> > -static relopt_real *
> > -init_real_reloption(bits32 kinds, const char *name, const char *desc,
> > -                    double default_val, double min_val,
double max_val,
> > -                    LOCKMODE lockmode)
> > -{
> > -    relopt_real *newoption;
> > -
> > -    newoption = (relopt_real *) allocate_reloption(kinds,
RELOPT_TYPE_REAL,
> > -
           name, desc, lockmode);
> > -    newoption->default_val = default_val;
> > -    newoption->min = min_val;
> > -    newoption->max = max_val;
> > -
> > -    return newoption;
> > -}
> > -
> > -/*
> > - * add_real_reloption
> > - *        Add a new float reloption
> > - */
> > -void
> > -add_real_reloption(bits32 kinds, const char *name, const char *desc,
> > -                   double default_val, double min_val,
double max_val,
> > -                   LOCKMODE lockmode)
> > -{
> > -    relopt_real *newoption = init_real_reloption(kinds, name, desc,
> > -
         default_val, min_val,
> > -
         max_val, lockmode);
> > -
> > -    add_reloption((relopt_gen *) newoption);
> > +    optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, 0, offset,
> > +
        default_val, min_val, max_val);
> >
> >  }
> >
> >  /*
> >
> > @@ -966,57 +193,9 @@ add_local_real_reloption(local_relopts *relopts,
> > const char *name,>
> >                           const char *desc, double
default_val,
> >                           double min_val, double
max_val, int offset)
> >
> >  {
> >
> > -    relopt_real *newoption = init_real_reloption(RELOPT_KIND_LOCAL,
> > -
         name, desc,
> > -
         default_val, min_val,
> > -
         max_val, 0);
> > -
> > -    add_local_reloption(relopts, (relopt_gen *) newoption, offset);
> > -}
> > -
> > -/*
> > - * init_enum_reloption
> > - *        Allocate and initialize a new enum reloption
> > - */
> > -static relopt_enum *
> > -init_enum_reloption(bits32 kinds, const char *name, const char *desc,
> > -                    relopt_enum_elt_def *members, int
default_val,
> > -                    const char *detailmsg, LOCKMODE
lockmode)
> > -{
> > -    relopt_enum *newoption;
> > -
> > -    newoption = (relopt_enum *) allocate_reloption(kinds,
RELOPT_TYPE_ENUM,
> > -
           name, desc, lockmode);
> > -    newoption->members = members;
> > -    newoption->default_val = default_val;
> > -    newoption->detailmsg = detailmsg;
> > -
> > -    return newoption;
> > -}
> > -
> > -
> > -/*
> > - * add_enum_reloption
> > - *        Add a new enum reloption
> > - *
> > - * The members array must have a terminating NULL entry.
> > - *
> > - * The detailmsg is shown when unsupported values are passed, and has
> > this
> > - * form:   "Valid values are \"foo\", \"bar\", and \"bar\"."
> > - *
> > - * The members array and detailmsg are not copied -- caller must ensure
> > that - * they are valid throughout the life of the process.
> > - */
> > -void
> > -add_enum_reloption(bits32 kinds, const char *name, const char *desc,
> > -                   relopt_enum_elt_def *members, int
default_val,
> > -                   const char *detailmsg, LOCKMODE lockmode)
> > -{
> > -    relopt_enum *newoption = init_enum_reloption(kinds, name, desc,
> > -
         members, default_val,
> > -
         detailmsg, lockmode);
> > +    optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, 0,
offset,
> > +
        default_val, min_val, max_val);
> >
> > -    add_reloption((relopt_gen *) newoption);
> >
> >  }
> >
> >  /*
> >
> > @@ -1027,77 +206,11 @@ add_enum_reloption(bits32 kinds, const char *name,
> > const char *desc,>
> >   */
> >
> >  void
> >  add_local_enum_reloption(local_relopts *relopts, const char *name,
> >
> > -                         const char *desc,
relopt_enum_elt_def *members,
> > +                         const char *desc,
opt_enum_elt_def *members,
> >
> >                           int default_val, const char
*detailmsg, int offset)
> >
> >  {
> >
> > -    relopt_enum *newoption = init_enum_reloption(RELOPT_KIND_LOCAL,
> > -
         name, desc,
> > -
         members, default_val,
> > -
         detailmsg, 0);
> > -
> > -    add_local_reloption(relopts, (relopt_gen *) newoption, offset);
> > -}
> > -
> > -/*
> > - * init_string_reloption
> > - *        Allocate and initialize a new string reloption
> > - */
> > -static relopt_string *
> > -init_string_reloption(bits32 kinds, const char *name, const char *desc,
> > -                      const char *default_val,
> > -                      validate_string_relopt validator,
> > -                      fill_string_relopt filler,
> > -                      LOCKMODE lockmode)
> > -{
> > -    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, lockmode);
> > -    newoption->validate_cb = validator;
> > -    newoption->fill_cb = filler;
> > -    if (default_val)
> > -    {
> > -        if (kinds == RELOPT_KIND_LOCAL)
> > -            newoption->default_val = strdup(default_val);
> > -        else
> > -            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;
> > -    }
> > -
> > -    return newoption;
> > -}
> > -
> > -/*
> > - * add_string_reloption
> > - *        Add a new string reloption
> > - *
> > - * "validator" is an optional function pointer that can be used to test
> > the - * validity of the values.  It must elog(ERROR) when the argument
> > string is - * not acceptable for the variable.  Note that the default
> > value must pass - * the validation.
> > - */
> > -void
> > -add_string_reloption(bits32 kinds, const char *name, const char *desc,
> > -                     const char *default_val,
validate_string_relopt validator,
> > -                     LOCKMODE lockmode)
> > -{
> > -    relopt_string *newoption = init_string_reloption(kinds, name, desc,
> > -
             default_val,
> > -
             validator, NULL,
> > -
             lockmode);
> > -
> > -    add_reloption((relopt_gen *) newoption);
> > +    optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, 0,
offset,
> > +
    members, default_val, detailmsg);
> >
> >  }
> >
> >  /*
> >
> > @@ -1113,249 +226,9 @@ add_local_string_reloption(local_relopts *relopts,
> > const char *name,>
> >                             validate_string_relopt
validator,
> >                             fill_string_relopt filler,
int offset)
> >
> >  {
> >
> > -    relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
> > -
             name, desc,
> > -
             default_val,
> > -
             validator, filler,
> > -
             0);
> > -
> > -    add_local_reloption(relopts, (relopt_gen *) newoption, offset);
> > -}
> > -
> > -/*
> > - * 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 acceptOidsOff is true, then we allow oids = false, but throw error
> > when - * on. This is solely needed for backwards compatibility.
> > - *
> > - * Note that this is not responsible for determining whether the options
> > - * are valid, but it does check that namespaces for all the options given
> > are - * listed in validnsps.  The NULL namespace is always valid and need
> > not be - * explicitly listed.  Passing a NULL pointer means that only the
> > NULL - * namespace is valid.
> > - *
> > - * Both oldOptions and the result are text arrays (or NULL for
> > "default"),
> > - * but we declare them as Datums to avoid including array.h in
> > reloptions.h. - */
> > -Datum
> > -transformRelOptions(Datum oldOptions, List *defList, const char
> > *namspace,
> > -                    char *validnsps[], bool
acceptOidsOff, 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, TYPALIGN_INT,
> > -                          &oldoptions, NULL,
&noldoptions);
> > -
> > -        for (i = 0; i < noldoptions; i++)
> > -        {
> > -            char       *text_str = VARDATA(oldoptions[i]);
> > -            int            text_len =
VARSIZE(oldoptions[i]) - VARHDRSZ;
> > -
> > -            /* Search for a match in defList */
> > -            foreach(cell, defList)
> > -            {
> > -                DefElem    *def = (DefElem *) lfirst(cell);
> > -                int            kw_len;
> > -
> > -                /* ignore if not in the same namespace */
> > -                if (namspace == NULL)
> > -                {
> > -                    if (def->defnamespace != NULL)
> > -                        continue;
> > -                }
> > -                else if (def->defnamespace == NULL)
> > -                    continue;
> > -                else if (strcmp(def->defnamespace, namspace)
!= 0)
> > -                    continue;
> > -
> > -                kw_len = strlen(def->defname);
> > -                if (text_len > kw_len && text_str[kw_len] ==
'=' &&
> > -                    strncmp(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 (strcmp(def-
>defnamespace, validnsps[i]) == 0)
> > -                        {
> > -                            valid = true;
> > -                            break;
> > -                        }
> > -                    }
> > -                }
> > -
> > -                if (!valid)
> > -                    ereport(ERROR,
> > -
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > -
errmsg("unrecognized parameter namespace \"%s\"",
> > -                                    def-
>defnamespace)));
> > -            }
> > -
> > -            /* ignore if not in the same namespace */
> > -            if (namspace == NULL)
> > -            {
> > -                if (def->defnamespace != NULL)
> > -                    continue;
> > -            }
> > -            else if (def->defnamespace == NULL)
> > -                continue;
> > -            else if (strcmp(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";
> > -
> > -            /*
> > -             * This is not a great place for this test, but
there's no other
> > -             * convenient place to filter the option out. As WITH
(oids =
> > -             * false) will be removed someday, this seems like
an acceptable
> > -             * amount of ugly.
> > -             */
> > -            if (acceptOidsOff && def->defnamespace == NULL &&
> > -                strcmp(def->defname, "oids") == 0)
> > -            {
> > -                if (defGetBoolean(def))
> > -                    ereport(ERROR,
> > -
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
> > -                             errmsg("tables
declared WITH OIDS are not supported")));
> > -                /* skip over option, reloptions machinery
doesn't know it */
> > -                continue;
> > -            }
> > -
> > -            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, TYPALIGN_INT,
> > -                      &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;
> > +    optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, 0,
> > offset,
> > +
    default_val, validator);
> > +/* FIXME solve mistery with filler option! */
> >
> >  }
> >
> >  /*
> >
> > @@ -1372,12 +245,13 @@ untransformRelOptions(Datum options)
> >
> >   */
> >
> >  bytea *
> >  extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
> >
> > -                  amoptions_function amoptions)
> > +                  amreloptspecset_function
amoptionsspecsetfn)
> >
> >  {
> >
> >      bytea       *options;
> >      bool        isnull;
> >      Datum        datum;
> >      Form_pg_class classForm;
> >
> > +    options_spec_set *spec_set;
> >
> >      datum = fastgetattr(tuple,
> >
> >                          Anum_pg_class_reloptions,
> >
> > @@ -1394,702 +268,341 @@ extractRelOptions(HeapTuple tuple, TupleDesc
> > tupdesc,>
> >          case RELKIND_RELATION:
> >          case RELKIND_TOASTVALUE:
> >
> >          case RELKIND_MATVIEW:
> > -            options = heap_reloptions(classForm->relkind, datum,
false);
> > +            spec_set = get_heap_relopt_spec_set();
> >
> >              break;
> >
> >          case RELKIND_PARTITIONED_TABLE:
> > -            options = partitioned_table_reloptions(datum,
false);
> > +            spec_set = get_partitioned_relopt_spec_set();
> >
> >              break;
> >
> >          case RELKIND_VIEW:
> > -            options = view_reloptions(datum, false);
> > +            spec_set = get_view_relopt_spec_set();
> >
> >              break;
> >
> >          case RELKIND_INDEX:
> >
> >          case RELKIND_PARTITIONED_INDEX:
> > -            options = index_reloptions(amoptions, datum, false);
> > +            if (amoptionsspecsetfn)
> > +                spec_set = amoptionsspecsetfn();
> > +            else
> > +                spec_set = NULL;
> >
> >              break;
> >
> >          case RELKIND_FOREIGN_TABLE:
> > -            options = NULL;
> > +            spec_set = NULL;
> >
> >              break;
> >
> >          default:
> >              Assert(false);        /* can't get here */
> >
> > -            options = NULL;        /* keep compiler quiet */
> > +            spec_set = NULL;        /* keep compiler quiet */
> >
> >              break;
> >
> >      }
> >
> > +    if (spec_set)
> > +        options = optionsTextArrayToBytea(spec_set, datum, 0);
> > +    else
> > +        options = NULL;
> >
> >      return options;
> >
> >  }
> >
> > -static void
> > -parseRelOptionsInternal(Datum options, bool validate,
> > -                        relopt_value *reloptions,
int numoptions)
> > -{
> > -    ArrayType  *array = DatumGetArrayTypeP(options);
> > -    Datum       *optiondatums;
> > -    int            noptions;
> > -    int            i;
> > -
> > -    deconstruct_array(array, TEXTOID, -1, false, TYPALIGN_INT,
> > -                      &optiondatums, NULL, &noptions);
> > +options_spec_set *
> > +get_stdrd_relopt_spec_set(relopt_kind kind)
> > +{
> > +    bool is_for_toast = (kind == RELOPT_KIND_TOAST);
> > +
> > +    options_spec_set * stdrd_relopt_spec_set = allocateOptionsSpecSet(
> > +                    is_for_toast ? "toast" : NULL,
sizeof(StdRdOptions), 0); //FIXME
> > change 0 to actual value (may be)
> > +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "fillfactor",
> > +                                 "Packs table
pages only to this percentag",
> > +
ShareUpdateExclusiveLock,        /* since it applies only
> > +
                         * to later inserts */
> > +                                is_for_toast
? OPTION_DEFINITION_FLAG_REJECT : 0,
> > +
offsetof(StdRdOptions, fillfactor),
> > +                          HEAP_DEFAULT_FILLFACTOR,
HEAP_MIN_FILLFACTOR, 100);
> > +    optionsSpecSetAddBool(stdrd_relopt_spec_set, "autovacuum_enabled",
> > +                              "Enables autovacuum
in this relation",
> > +
ShareUpdateExclusiveLock, 0,
> > +            offsetof(StdRdOptions, autovacuum) +
offsetof(AutoVacOpts, enabled),
> > +                              true);
> > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
> > "autovacuum_vacuum_threshold", +                "Minimum number
of tuple updates or
> > deletes prior to vacuum", +
     ShareUpdateExclusiveLock,
> > +                    0, offsetof(StdRdOptions, autovacuum)
+ offsetof(AutoVacOpts,
> > vacuum_threshold), +
-1, 0, INT_MAX);
> > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
> > "autovacuum_analyze_threshold", +                "Minimum number
of tuple updates or
> > deletes prior to vacuum", +
     ShareUpdateExclusiveLock,
> > +                             is_for_toast ?
OPTION_DEFINITION_FLAG_REJECT : 0,
> > +                      offsetof(StdRdOptions, autovacuum) +
offsetof(AutoVacOpts,
> > analyze_threshold), +
-1, 0, INT_MAX);
> > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
> > "autovacuum_vacuum_cost_limit", +               "Vacuum cost amount
available
> > before napping, for autovacuum", +
     ShareUpdateExclusiveLock,
> > +                   0, offsetof(StdRdOptions, autovacuum) +
offsetof(AutoVacOpts,
> > vacuum_cost_limit), +
-1, 0, 10000);
> > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
"autovacuum_freeze_min_age",
> > +     "Minimum age at which VACUUM should freeze a table row, for
> > autovacuum",
> > +
ShareUpdateExclusiveLock,
> > +                      0, offsetof(StdRdOptions,
autovacuum) + offsetof(AutoVacOpts,
> > freeze_min_age), +
-1, 0, 1000000000);
> > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
"autovacuum_freeze_max_age",
> > +    "Age at which to autovacuum a table to prevent transaction ID
> > wraparound", +
ShareUpdateExclusiveLock,
> > +                      0, offsetof(StdRdOptions,
autovacuum) + offsetof(AutoVacOpts,
> > freeze_max_age), +
-1, 100000, 2000000000);
> > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
> > "autovacuum_freeze_table_age", +
 "Age at which VACUUM should
> > perform a full table sweep to freeze row versions", +

> > ShareUpdateExclusiveLock,
> > +                    0, offsetof(StdRdOptions, autovacuum)
+ offsetof(AutoVacOpts,
> > freeze_table_age), +
-1, 0, 2000000000);
> > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
> > "autovacuum_multixact_freeze_min_age", +
         "Minimum multixact age at
> > which VACUUM should freeze a row multixact's, for autovacuum", +

> > ShareUpdateExclusiveLock,
> > +            0, offsetof(StdRdOptions, autovacuum) +
offsetof(AutoVacOpts,
> > multixact_freeze_min_age), +
 -1, 0, 1000000000);
> > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
> > "autovacuum_multixact_freeze_max_age", +
     "Multixact age at which
> > to autovacuum a table to prevent multixact wraparound", +

> > ShareUpdateExclusiveLock,
> > +            0, offsetof(StdRdOptions, autovacuum) +
offsetof(AutoVacOpts,
> > multixact_freeze_max_age), +
 -1, 10000, 2000000000);
> > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
> > "autovacuum_multixact_freeze_table_age", +
         "Age of multixact at
> > which VACUUM should perform a full table sweep to freeze row versions",
> > +
ShareUpdateExclusiveLock,
> > +          0, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > multixact_freeze_table_age), +
     -1, 0, 2000000000);
> > +
optionsSpecSetAddInt(stdrd_relopt_spec_set,"log_autovacuum_min_duration"
> > ,
> > +                             "Sets the minimum
execution time above which autovacuum actions
> > will be logged", +
ShareUpdateExclusiveLock,
> > +                    0, offsetof(StdRdOptions, autovacuum)
+ offsetof(AutoVacOpts,
> > log_min_duration), +
-1, -1, INT_MAX);
> > +    optionsSpecSetAddReal(stdrd_relopt_spec_set,
> > "autovacuum_vacuum_cost_delay", +
 "Vacuum cost delay in
> > milliseconds, for autovacuum",
> > +
ShareUpdateExclusiveLock,
> > +                   0, offsetof(StdRdOptions, autovacuum) +
offsetof(AutoVacOpts,
> > vacuum_cost_delay), +
-1, 0.0, 100.0);
> > +    optionsSpecSetAddReal(stdrd_relopt_spec_set,
> > "autovacuum_vacuum_scale_factor", +
      "Number of tuple updates or
> > deletes prior to vacuum as a fraction of reltuples", +

> > ShareUpdateExclusiveLock,
> > +                 0, offsetof(StdRdOptions, autovacuum) +
offsetof(AutoVacOpts,
> > vacuum_scale_factor), +
-1, 0.0, 100.0);
> > +
> > +    optionsSpecSetAddReal(stdrd_relopt_spec_set,
> > "autovacuum_vacuum_insert_scale_factor", +
          "Number of tuple
> > inserts prior to vacuum as a fraction of reltuples", +

> > ShareUpdateExclusiveLock,
> > +                 0, offsetof(StdRdOptions, autovacuum) +
offsetof(AutoVacOpts,
> > vacuum_ins_scale_factor), +
  -1, 0.0, 100.0);
> > +
> > +    optionsSpecSetAddReal(stdrd_relopt_spec_set,
> > "autovacuum_analyze_scale_factor", +
          "Number of tuple inserts,
> > updates or deletes prior to analyze as a fraction of reltuples", +

> >  ShareUpdateExclusiveLock,
> > +                              is_for_toast ?
OPTION_DEFINITION_FLAG_REJECT : 0,
> > +                   offsetof(StdRdOptions, autovacuum) +
offsetof(AutoVacOpts,
> > analyze_scale_factor), +
  -1, 0.0, 100.0);
> > +
> > +
> > +
> > +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "toast_tuple_target",
> > +                                 "Sets the
target tuple length at which external columns will be
> > toasted", +
ShareUpdateExclusiveLock,
> > +                                is_for_toast
? OPTION_DEFINITION_FLAG_REJECT : 0,
> > +
offsetof(StdRdOptions, toast_tuple_target),
> > +                          TOAST_TUPLE_TARGET, 128,
TOAST_TUPLE_TARGET_MAIN);
> > +
> > +    optionsSpecSetAddBool(stdrd_relopt_spec_set, "user_catalog_table",
> > +                                  "Declare a
table as an additional catalog table, e.g. for the
> > purpose of logical replication", +
          AccessExclusiveLock,
> > +                                is_for_toast
? OPTION_DEFINITION_FLAG_REJECT : 0,
> > +
offsetof(StdRdOptions, user_catalog_table),
> > +                                  false);
> > +
> > +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "parallel_workers",
> > +                                "Number of
parallel processes that can be used per executor node
> > for this relation.", +
    ShareUpdateExclusiveLock,
> > +                                is_for_toast
? OPTION_DEFINITION_FLAG_REJECT : 0,
> > +
offsetof(StdRdOptions, parallel_workers),
> > +                                -1, 0, 1024);
> > +
> > +    optionsSpecSetAddEnum(stdrd_relopt_spec_set, "vacuum_index_cleanup",
> > +                                "Controls
index vacuuming and index cleanup",
> > +
ShareUpdateExclusiveLock, 0,
> > +
offsetof(StdRdOptions, vacuum_index_cleanup),
> > +
StdRdOptIndexCleanupValues,
> > +
STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO,
> > +
gettext_noop("Valid values are \"on\", \"off\", and \"auto\"."));
> > +
> > +    optionsSpecSetAddBool(stdrd_relopt_spec_set, "vacuum_truncate",
> > +                                "Enables
vacuum to truncate empty pages at the end of this
> > table",
> > +
ShareUpdateExclusiveLock, 0,
> > +
offsetof(StdRdOptions, vacuum_truncate),
> > +                                true);
> > +
> > +// FIXME Do something with OIDS
> > +
> > +    return stdrd_relopt_spec_set;
> > +}
> > +
> > +
> > +static options_spec_set *heap_relopt_spec_set = NULL;
> > +
> > +options_spec_set *
> > +get_heap_relopt_spec_set(void)
> > +{
> > +    if (heap_relopt_spec_set)
> > +        return heap_relopt_spec_set;
> > +    heap_relopt_spec_set = get_stdrd_relopt_spec_set(RELOPT_KIND_HEAP);
> > +    return heap_relopt_spec_set;
> > +}
> > +
> > +static options_spec_set *toast_relopt_spec_set = NULL;
> > +
> > +options_spec_set *
> > +get_toast_relopt_spec_set(void)
> > +{
> > +    if (toast_relopt_spec_set)
> > +        return toast_relopt_spec_set;
> > +    toast_relopt_spec_set = get_stdrd_relopt_spec_set(RELOPT_KIND_TOAST);
> > +    return toast_relopt_spec_set;
> > +}
> > +
> > +static options_spec_set *partitioned_relopt_spec_set = NULL;
> >
> > -    for (i = 0; i < noptions; i++)
> > -    {
> > -        char       *text_str = VARDATA(optiondatums[i]);
> > -        int            text_len = VARSIZE(optiondatums[i])
- VARHDRSZ;
> > -        int            j;
> > -
> > -        /* Search for a match in reloptions */
> > -        for (j = 0; j < numoptions; j++)
> > -        {
> > -            int            kw_len = reloptions[j].gen-
>namelen;
> > -
> > -            if (text_len > kw_len && text_str[kw_len] == '=' &&
> > -                strncmp(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);
> > +options_spec_set *
> > +get_partitioned_relopt_spec_set(void)
> > +{
> > +    if (partitioned_relopt_spec_set)
> > +        return partitioned_relopt_spec_set;
> > +    partitioned_relopt_spec_set = allocateOptionsSpecSet(
> > +                    NULL,  sizeof(StdRdOptions), 0);
> > +    /* No options for now, so spec set is empty */
> >
> > -    if (((void *) array) != DatumGetPointer(options))
> > -        pfree(array);
> > +    return partitioned_relopt_spec_set;
> >
> >  }
> >
> >  /*
> >
> > - * Interpret reloptions that are given in text-array format.
> > - *
> > - * options is a reloption text array as constructed by
> > transformRelOptions. - * kind specifies the family of options to be
> > processed.
> > - *
> > - * The return value is a relopt_value * array on which the options
> > actually - * set in the options array are marked with isset=true.  The
> > length of this - * array is returned in *numrelopts.  Options not set are
> > also present in the - * array; this is so that the caller can easily
> > locate the default values. - *
> > - * If there are no options of the given kind, numrelopts is set to 0 and
> > NULL - * is returned (unless options are illegally supplied despite none
> > being - * defined, in which case an error occurs).
> > - *
> > - * Note: values of type int, bool and real are allocated as part of the
> > - * returned array.  Values of type string are allocated separately and
> > must - * be freed by the caller.
> > + * Parse local options, allocate a bytea struct that's of the specified
> > + * 'base_size' plus any extra space that's needed for string variables,
> > + * fill its option's fields located at the given offsets and return it.
> >
> >   */
> >
> > -static relopt_value *
> > -parseRelOptions(Datum options, bool validate, relopt_kind kind,
> > -                int *numrelopts)
> > -{
> > -    relopt_value *reloptions = NULL;
> > -    int            numoptions = 0;
> > -    int            i;
> > -    int            j;
> > -
> > -    if (need_initialization)
> > -        initialize_reloptions();
> > -
> > -    /* Build a list of expected options, based on kind */
> > -
> > -    for (i = 0; relOpts[i]; i++)
> > -        if (relOpts[i]->kinds & kind)
> > -            numoptions++;
> > -
> > -    if (numoptions > 0)
> > -    {
> > -        reloptions = palloc(numoptions * sizeof(relopt_value));
> > -
> > -        for (i = 0, j = 0; relOpts[i]; i++)
> > -        {
> > -            if (relOpts[i]->kinds & kind)
> > -            {
> > -                reloptions[j].gen = relOpts[i];
> > -                reloptions[j].isset = false;
> > -                j++;
> > -            }
> > -        }
> > -    }
> > -
> > -    /* Done if no options */
> > -    if (PointerIsValid(DatumGetPointer(options)))
> > -        parseRelOptionsInternal(options, validate, reloptions,
numoptions);
> > -
> > -    *numrelopts = numoptions;
> > -    return reloptions;
> > -}
> > -
> > -/* Parse local unregistered options. */
> > -static relopt_value *
> > -parseLocalRelOptions(local_relopts *relopts, Datum options, bool
> > validate)
> > +void *
> > +build_local_reloptions(local_relopts *relopts, Datum options, bool
> > validate)>
> >  {
> >
> > -    int            nopts = list_length(relopts->options);
> > -    relopt_value *values = palloc(sizeof(*values) * nopts);
> > +    void       *opts;
> >
> >      ListCell   *lc;
> >
> > -    int            i = 0;
> > -
> > -    foreach(lc, relopts->options)
> > -    {
> > -        local_relopt *opt = lfirst(lc);
> > -
> > -        values[i].gen = opt->option;
> > -        values[i].isset = false;
> > -
> > -        i++;
> > -    }
> > -
> > -    if (options != (Datum) 0)
> > -        parseRelOptionsInternal(options, validate, values, nopts);
> > +    opts = (void *) optionsTextArrayToBytea(relopts->spec_set, options,
> > validate);
> >
> > -    return values;
> > -}
> > -
> > -/*
> > - * Subroutine for parseRelOptions, to parse and validate a single
> > option's
> > - * value
> > - */
> > -static void
> > -parse_one_reloption(relopt_value *option, char *text_str, int text_len,
> > -                    bool validate)
> > -{
> > -    char       *value;
> > -    int            value_len;
> > -    bool        parsed;
> > -    bool        nofree = false;
> > -
> > -    if (option->isset && validate)
> > -        ereport(ERROR,
> > -                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > -                 errmsg("parameter \"%s\" specified more than
once",
> > -                        option->gen->name)));
> > -
> > -    value_len = text_len - option->gen->namelen - 1;
> > -    value = (char *) palloc(value_len + 1);
> > -    memcpy(value, text_str + option->gen->namelen + 1, value_len);
> > -    value[value_len] = '\0';
> > -
> > -    switch (option->gen->type)
> > -    {
> > -        case RELOPT_TYPE_BOOL:
> > -            {
> > -                parsed = parse_bool(value, &option-
>values.bool_val);
> > -                if (validate && !parsed)
> > -                    ereport(ERROR,
> > -
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > -                             errmsg("invalid
value for boolean option \"%s\": %s",
> > -
option->gen->name, value)));
> > -            }
> > -            break;
> > -        case RELOPT_TYPE_INT:
> > -            {
> > -                relopt_int *optint = (relopt_int *) option-
>gen;
> > -
> > -                parsed = parse_int(value, &option-
>values.int_val, 0, NULL);
> > -                if (validate && !parsed)
> > -                    ereport(ERROR,
> > -
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > -                             errmsg("invalid
value for integer option \"%s\": %s",
> > -
option->gen->name, value)));
> > -                if (validate && (option->values.int_val <
optint->min ||
> > -                                 option-
>values.int_val > optint->max))
> > -                    ereport(ERROR,
> > -
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > -                             errmsg("value %s
out of bounds for option \"%s\"",
> > -
value, option->gen->name),
> > -                             errdetail("Valid
values are between \"%d\" and \"%d\".",
> > -
optint->min, optint->max)));
> > -            }
> > -            break;
> > -        case RELOPT_TYPE_REAL:
> > -            {
> > -                relopt_real *optreal = (relopt_real *)
option->gen;
> > -
> > -                parsed = parse_real(value, &option-
>values.real_val, 0, NULL);
> > -                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_ENUM:
> > -            {
> > -                relopt_enum *optenum = (relopt_enum *)
option->gen;
> > -                relopt_enum_elt_def *elt;
> > -
> > -                parsed = false;
> > -                for (elt = optenum->members; elt-
>string_val; elt++)
> > -                {
> > -                    if (pg_strcasecmp(value, elt-
>string_val) == 0)
> > -                    {
> > -                        option->values.enum_val =
elt->symbol_val;
> > -                        parsed = true;
> > -                        break;
> > -                    }
> > -                }
> > -                if (validate && !parsed)
> > -                    ereport(ERROR,
> > -
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > -                             errmsg("invalid
value for enum option \"%s\": %s",
> > -
option->gen->name, value),
> > -                             optenum->detailmsg
?
> > -
errdetail_internal("%s", _(optenum->detailmsg)) : 0));
> > -
> > -                /*
> > -                 * If value is not among the allowed string
values, but we are
> > -                 * not asked to validate, just use the
default numeric value.
> > -                 */
> > -                if (!parsed)
> > -                    option->values.enum_val = optenum-
>default_val;
> > -            }
> > -            break;
> > -        case RELOPT_TYPE_STRING:
> > -            {
> > -                relopt_string *optstring = (relopt_string *)
option->gen;
> > -
> > -                option->values.string_val = value;
> > -                nofree = true;
> > -                if (validate && optstring->validate_cb)
> > -                    (optstring->validate_cb) (value);
> > -                parsed = true;
> > -            }
> > -            break;
> > -        default:
> > -            elog(ERROR, "unsupported reloption type %d", option-
>gen->type);
> > -            parsed = true;        /* quiet compiler */
> > -            break;
> > -    }
> > +    foreach(lc, relopts->validators)
> > +        ((relopts_validator) lfirst(lc)) (opts, NULL, 0);
> > +//        ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
> > +// FIXME solve problem with validation of separate option values;
> > +    return opts;
> >
> > -    if (parsed)
> > -        option->isset = true;
> > -    if (!nofree)
> > -        pfree(value);
> >
> >  }
> >
> >  /*
> >
> > - * Given the result from parseRelOptions, allocate a struct that's of the
> > - * specified base size plus any extra space that's needed for string
> > variables. - *
> > - * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions
> > or - * equivalent).
> > + * get_view_relopt_spec_set
> > + *        Returns an options catalog for view relation.
> >
> >   */
> >
> > -static void *
> > -allocateReloptStruct(Size base, relopt_value *options, int numoptions)
> > -{
> > -    Size        size = base;
> > -    int            i;
> > -
> > -    for (i = 0; i < numoptions; i++)
> > -    {
> > -        relopt_value *optval = &options[i];
> > -
> > -        if (optval->gen->type == RELOPT_TYPE_STRING)
> > -        {
> > -            relopt_string *optstr = (relopt_string *) optval-
>gen;
> > -
> > -            if (optstr->fill_cb)
> > -            {
> > -                const char *val = optval->isset ? optval-
>values.string_val :
> > -                optstr->default_isnull ? NULL : optstr-
>default_val;
> > -
> > -                size += optstr->fill_cb(val, NULL);
> > -            }
> > -            else
> > -                size += GET_STRING_RELOPTION_LEN(*optval) +
1;
> > -        }
> > -    }
> > -
> > -    return palloc0(size);
> > -}
> > +static options_spec_set *view_relopt_spec_set = NULL;
> >
> > -/*
> > - * 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.
> > - */
> > -static void
> > -fillRelOptions(void *rdopts, Size basesize,
> > -               relopt_value *options, int numoptions,
> > -               bool validate,
> > -               const relopt_parse_elt *elems, int numelems)
> > +options_spec_set *
> > +get_view_relopt_spec_set(void)
> >
> >  {
> >
> > -    int            i;
> > -    int            offset = basesize;
> > +    if (view_relopt_spec_set)
> > +        return view_relopt_spec_set;
> >
> > -    for (i = 0; i < numoptions; i++)
> > -    {
> > -        int            j;
> > -        bool        found = false;
> > +    view_relopt_spec_set = allocateOptionsSpecSet(NULL,
> > +
         sizeof(ViewOptions), 2);
> >
> > -        for (j = 0; j < numelems; j++)
> > -        {
> > -            if (strcmp(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_ENUM:
> > -                        *(int *) itempos =
options[i].isset ?
> > -
options[i].values.enum_val :
> > -                            ((relopt_enum *)
options[i].gen)->default_val;
> > -                        break;
> > -                    case RELOPT_TYPE_STRING:
> > -                        optstring = (relopt_string
*) options[i].gen;
> > -                        if (options[i].isset)
> > -                            string_val =
options[i].values.string_val;
> > -                        else if (!optstring-
>default_isnull)
> > -                            string_val =
optstring->default_val;
> > -                        else
> > -                            string_val = NULL;
> > -
> > -                        if (optstring->fill_cb)
> > -                        {
> > -                            Size
size =
> > -                            optstring-
>fill_cb(string_val,
> > -
       (char *) rdopts + offset);
> > -
> > -                            if (size)
> > -                            {
> > -                                *(int *)
itempos = offset;
> > -                                offset +=
size;
> > -                            }
> > -                            else
> > -                                *(int *)
itempos = 0;
> > -                        }
> > -                        else if (string_val == NULL)
> > -                            *(int *) itempos =
0;
> > -                        else
> > -                        {
> > -                            strcpy((char *)
rdopts + offset, string_val);
> > -                            *(int *) itempos =
offset;
> > -                            offset +=
strlen(string_val) + 1;
> > -                        }
> > -                        break;
> > -                    default:
> > -                        elog(ERROR, "unsupported
reloption type %d",
> > -                             options[i].gen-
>type);
> > -                        break;
> > -                }
> > -                found = true;
> > -                break;
> > -            }
> > -        }
> > -        if (validate && !found)
> > -            elog(ERROR, "reloption \"%s\" not found in parse
table",
> > -                 options[i].gen->name);
> > -    }
> > -    SET_VARSIZE(rdopts, offset);
> > -}
> > +    optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
> > +                              "View acts as a row
security barrier",
> > +
AccessExclusiveLock,
> > +                      0, offsetof(ViewOptions,
security_barrier), false);
> >
> > +    optionsSpecSetAddEnum(view_relopt_spec_set, "check_option",
> > +                           "View has WITH CHECK
OPTION defined (local or cascaded)",
> > +
AccessExclusiveLock, 0,
> > +
offsetof(ViewOptions, check_option),
> > +                              viewCheckOptValues,
> > +
VIEW_OPTION_CHECK_OPTION_NOT_SET,
> > +                              gettext_noop("Valid
values are \"local\" and \"cascaded\"."));
> >
> > -/*
> > - * Option parser for anything that uses StdRdOptions.
> > - */
> > -bytea *
> > -default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
> > -{
> > -    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_vacuum_insert_threshold",
> > RELOPT_TYPE_INT,
> > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > vacuum_ins_threshold)}, -        {"autovacuum_analyze_threshold",
> > RELOPT_TYPE_INT,
> > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > analyze_threshold)}, -        {"autovacuum_vacuum_cost_limit",
RELOPT_TYPE_INT,
> > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > vacuum_cost_limit)}, -        {"autovacuum_freeze_min_age",
RELOPT_TYPE_INT,
> > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > freeze_min_age)}, -        {"autovacuum_freeze_max_age",
RELOPT_TYPE_INT,
> > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > freeze_max_age)}, -        {"autovacuum_freeze_table_age",
RELOPT_TYPE_INT,
> > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > freeze_table_age)}, -        {"autovacuum_multixact_freeze_min_age",
> > RELOPT_TYPE_INT,
> > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > multixact_freeze_min_age)}, -
{"autovacuum_multixact_freeze_max_age",
> > RELOPT_TYPE_INT,
> > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > multixact_freeze_max_age)}, -
{"autovacuum_multixact_freeze_table_age",
> > RELOPT_TYPE_INT,
> > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > multixact_freeze_table_age)}, -
{"log_autovacuum_min_duration",
> > RELOPT_TYPE_INT,
> > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > log_min_duration)}, -        {"toast_tuple_target", RELOPT_TYPE_INT,
> > -        offsetof(StdRdOptions, toast_tuple_target)},
> > -        {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_REAL,
> > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > vacuum_cost_delay)}, -        {"autovacuum_vacuum_scale_factor",
> > RELOPT_TYPE_REAL,
> > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > vacuum_scale_factor)}, -
{"autovacuum_vacuum_insert_scale_factor",
> > RELOPT_TYPE_REAL,
> > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > vacuum_ins_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)},
> > -        {"vacuum_index_cleanup", RELOPT_TYPE_ENUM,
> > -        offsetof(StdRdOptions, vacuum_index_cleanup)},
> > -        {"vacuum_truncate", RELOPT_TYPE_BOOL,
> > -        offsetof(StdRdOptions, vacuum_truncate)}
> > -    };
> > -
> > -    return (bytea *) build_reloptions(reloptions, validate, kind,
> > -
sizeof(StdRdOptions),
> > -
tab, lengthof(tab));
> > +    return view_relopt_spec_set;
> >
> >  }
> >
> >  /*
> >
> > - * build_reloptions
> > - *
> > - * Parses "reloptions" provided by the caller, returning them in a
> > - * structure containing the parsed options.  The parsing is done with
> > - * the help of a parsing table describing the allowed options, defined
> > - * by "relopt_elems" of length "num_relopt_elems".
> > - *
> > - * "validate" must be true if reloptions value is freshly built by
> > - * transformRelOptions(), as opposed to being read from the catalog, in
> > which - * case the values contained in it must already be valid.
> > - *
> > - * NULL is returned if the passed-in options did not match any of the
> > options - * in the parsing table, unless validate is true in which case
> > an error would - * be reported.
> > + * get_attribute_options_spec_set
> > + *        Returns an options spec det for heap attributes
> >
> >   */
> >
> > -void *
> > -build_reloptions(Datum reloptions, bool validate,
> > -                 relopt_kind kind,
> > -                 Size relopt_struct_size,
> > -                 const relopt_parse_elt *relopt_elems,
> > -                 int num_relopt_elems)
> > -{
> > -    int            numoptions;
> > -    relopt_value *options;
> > -    void       *rdopts;
> > -
> > -    /* parse options specific to given relation option kind */
> > -    options = parseRelOptions(reloptions, validate, kind, &numoptions);
> > -    Assert(numoptions <= num_relopt_elems);
> > -
> > -    /* if none set, we're done */
> > -    if (numoptions == 0)
> > -    {
> > -        Assert(options == NULL);
> > -        return NULL;
> > -    }
> > -
> > -    /* allocate and fill the structure */
> > -    rdopts = allocateReloptStruct(relopt_struct_size, options,
numoptions);
> > -    fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
> > -                   validate, relopt_elems,
num_relopt_elems);
> > +static options_spec_set *attribute_options_spec_set = NULL;
> >
> > -    pfree(options);
> > -
> > -    return rdopts;
> > -}
> > -
> > -/*
> > - * Parse local options, allocate a bytea struct that's of the specified
> > - * 'base_size' plus any extra space that's needed for string variables,
> > - * fill its option's fields located at the given offsets and return it.
> > - */
> > -void *
> > -build_local_reloptions(local_relopts *relopts, Datum options, bool
> > validate) +options_spec_set *
> > +get_attribute_options_spec_set(void)
> >
> >  {
> >
> > -    int            noptions = list_length(relopts->options);
> > -    relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
> > -    relopt_value *vals;
> > -    void       *opts;
> > -    int            i = 0;
> > -    ListCell   *lc;
> > +    if (attribute_options_spec_set)
> > +            return attribute_options_spec_set;
> >
> > -    foreach(lc, relopts->options)
> > -    {
> > -        local_relopt *opt = lfirst(lc);
> > -
> > -        elems[i].optname = opt->option->name;
> > -        elems[i].opttype = opt->option->type;
> > -        elems[i].offset = opt->offset;
> > -
> > -        i++;
> > -    }
> > +    attribute_options_spec_set = allocateOptionsSpecSet(NULL,
> > +
       sizeof(AttributeOpts), 2);
> >
> > -    vals = parseLocalRelOptions(relopts, options, validate);
> > -    opts = allocateReloptStruct(relopts->relopt_struct_size, vals,
> > noptions);
> > -    fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions,
> > validate, -                   elems, noptions);
> > +    optionsSpecSetAddReal(attribute_options_spec_set, "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);
> >
> > -    foreach(lc, relopts->validators)
> > -        ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
> > -
> > -    if (elems)
> > -        pfree(elems);
> > +    optionsSpecSetAddReal(attribute_options_spec_set,
> > +                          "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 opts;
> > +    return attribute_options_spec_set;
> >
> >  }
> >
> > -/*
> > - * Option parser for partitioned tables
> > - */
> > -bytea *
> > -partitioned_table_reloptions(Datum reloptions, bool validate)
> > -{
> > -    /*
> > -     * There are no options for partitioned tables yet, but this is able
to
> > do -     * some validation.
> > -     */
> > -    return (bytea *) build_reloptions(reloptions, validate,
> > -
RELOPT_KIND_PARTITIONED,
> > -                                      0,
NULL, 0);
> > -}
> >
> >  /*
> >
> > - * Option parser for views
> > - */
> > -bytea *
> > -view_reloptions(Datum reloptions, bool validate)
> > -{
> > -    static const relopt_parse_elt tab[] = {
> > -        {"security_barrier", RELOPT_TYPE_BOOL,
> > -        offsetof(ViewOptions, security_barrier)},
> > -        {"check_option", RELOPT_TYPE_ENUM,
> > -        offsetof(ViewOptions, check_option)}
> > -    };
> > -
> > -    return (bytea *) build_reloptions(reloptions, validate,
> > -
RELOPT_KIND_VIEW,
> > -
sizeof(ViewOptions),
> > -
tab, lengthof(tab));
> > -}
> > + * get_tablespace_options_spec_set
> > + *        Returns an options spec set for tablespaces
> > +*/
> > +static options_spec_set *tablespace_options_spec_set = NULL;
> >
> > -/*
> > - * Parse options for heaps, views and toast tables.
> > - */
> > -bytea *
> > -heap_reloptions(char relkind, Datum reloptions, bool validate)
> > +options_spec_set *
> > +get_tablespace_options_spec_set(void)
> >
> >  {
> >
> > -    StdRdOptions *rdopts;
> > -
> > -    switch (relkind)
> > +    if (!tablespace_options_spec_set)
> >
> >      {
> >
> > -        case RELKIND_TOASTVALUE:
> > -            rdopts = (StdRdOptions *)
> > -                default_reloptions(reloptions, validate,
RELOPT_KIND_TOAST);
> > -            if (rdopts != NULL)
> > -            {
> > -                /* adjust default-only parameters for TOAST
relations */
> > -                rdopts->fillfactor = 100;
> > -                rdopts->autovacuum.analyze_threshold = -1;
> > -                rdopts->autovacuum.analyze_scale_factor =
-1;
> > -            }
> > -            return (bytea *) rdopts;
> > -        case RELKIND_RELATION:
> > -        case RELKIND_MATVIEW:
> > -            return default_reloptions(reloptions, validate,
RELOPT_KIND_HEAP);
> > -        default:
> > -            /* other relkinds are not supported */
> > -            return NULL;
> > -    }
> > -}
> > -
> > -
> > -/*
> > - * Parse options for indexes.
> > - *
> > - *    amoptions    index AM's option parser function
> > - *    reloptions    options as text[] datum
> > - *    validate    error flag
> > - */
> > -bytea *
> > -index_reloptions(amoptions_function amoptions, Datum reloptions, bool
> > validate) -{
> > -    Assert(amoptions != NULL);
> > +        tablespace_options_spec_set = allocateOptionsSpecSet(NULL,
> > +
          sizeof(TableSpaceOpts), 4);
> >
> > -    /* Assume function is strict */
> > -    if (!PointerIsValid(DatumGetPointer(reloptions)))
> > -        return NULL;
> > +        optionsSpecSetAddReal(tablespace_options_spec_set,
> > +
"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);
> >
> > -    return amoptions(reloptions, validate);
> > -}
> > +        optionsSpecSetAddReal(tablespace_options_spec_set,
"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);
> >
> > -/*
> > - * Option parser for attribute reloptions
> > - */
> > -bytea *
> > -attribute_reloptions(Datum reloptions, bool validate)
> > -{
> > -    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)} -    };
> > -
> > -    return (bytea *) build_reloptions(reloptions, validate,
> > -
RELOPT_KIND_ATTRIBUTE,
> > -
sizeof(AttributeOpts),
> > -
tab, lengthof(tab));
> > -}
> > +        optionsSpecSetAddInt(tablespace_options_spec_set,
> > +
"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
> > +            );
> >
> > -/*
> > - * Option parser for tablespace reloptions
> > - */
> > -bytea *
> > -tablespace_reloptions(Datum reloptions, bool validate)
> > -{
> > -    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)},
> > -        {"maintenance_io_concurrency", RELOPT_TYPE_INT,
> > offsetof(TableSpaceOpts, maintenance_io_concurrency)} -    };
> > -
> > -    return (bytea *) build_reloptions(reloptions, validate,
> > -
RELOPT_KIND_TABLESPACE,
> > -
sizeof(TableSpaceOpts),
> > -
tab, lengthof(tab));
> > +        optionsSpecSetAddInt(tablespace_options_spec_set,
> > +
"maintenance_io_concurrency",
> > +                                 "Number of
simultaneous requests that can be handled efficiently
> > by the disk subsystem for maintenance work.", +

> > ShareUpdateExclusiveLock,
> > +                       0, offsetof(TableSpaceOpts,
maintenance_io_concurrency),
> > +#ifdef USE_PREFETCH
> > +                                 -1, 0,
MAX_IO_CONCURRENCY
> > +#else
> > +                                 0, 0, 0
> > +#endif
> > +            );
> > +    }
> > +    return tablespace_options_spec_set;
> >
> >  }
> >
> >  /*
> >
> > @@ -2099,33 +612,55 @@ tablespace_reloptions(Datum reloptions, bool
> > validate)>
> >   * for a longer explanation of how this works.
> >   */
> >
> >  LOCKMODE
> >
> > -AlterTableGetRelOptionsLockLevel(List *defList)
> > +AlterTableGetRelOptionsLockLevel(Relation rel, List *defList)
> >
> >  {
> >
> >      LOCKMODE    lockmode = NoLock;
> >      ListCell   *cell;
> >
> > +    options_spec_set *spec_set = NULL;
> >
> >      if (defList == NIL)
> >
> >          return AccessExclusiveLock;
> >
> > -    if (need_initialization)
> > -        initialize_reloptions();
> > +    switch (rel->rd_rel->relkind)
> > +    {
> > +        case RELKIND_TOASTVALUE:
> > +            spec_set = get_toast_relopt_spec_set();
> > +            break;
> > +        case RELKIND_RELATION:
> > +        case RELKIND_MATVIEW:
> > +            spec_set = get_heap_relopt_spec_set();
> > +            break;
> > +        case RELKIND_INDEX:
> > +            spec_set = rel->rd_indam->amreloptspecset();
> > +            break;
> > +        case RELKIND_VIEW:
> > +            spec_set = get_view_relopt_spec_set();
> > +            break;
> > +        case RELKIND_PARTITIONED_TABLE:
> > +            spec_set = get_partitioned_relopt_spec_set();
> > +            break;
> > +        default:
> > +            Assert(false);        /* can't get here */
> > +            break;
> > +    }
> > +    Assert(spec_set);            /* No spec set - no reloption
change. Should
> > +                                 * never get
here */
> >
> >      foreach(cell, defList)
> >      {
> >
> >          DefElem    *def = (DefElem *) lfirst(cell);
> >
> > +
> >
> >          int            i;
> >
> > -        for (i = 0; relOpts[i]; i++)
> > +        for (i = 0; i < spec_set->num; i++)
> >
> >          {
> >
> > -            if (strncmp(relOpts[i]->name,
> > -                        def->defname,
> > -                        relOpts[i]->namelen + 1) ==
0)
> > -            {
> > -                if (lockmode < relOpts[i]->lockmode)
> > -                    lockmode = relOpts[i]->lockmode;
> > -            }
> > +            option_spec_basic *gen = spec_set->definitions[i];
> > +
> > +            if (pg_strcasecmp(gen->name,
> > +                              def->defname) == 0)
> > +                if (lockmode < gen->lockmode)
> > +                    lockmode = gen->lockmode;
> >
> >          }
> >
> >      }
> >
> > -
> >
> >      return lockmode;
> >
> > -}
> > +}
> > \ No newline at end of file
> > diff --git a/src/backend/access/gin/gininsert.c
> > b/src/backend/access/gin/gininsert.c index 0e8672c..0cbffad 100644
> > --- a/src/backend/access/gin/gininsert.c
> > +++ b/src/backend/access/gin/gininsert.c
> > @@ -512,6 +512,8 @@ gininsert(Relation index, Datum *values, bool *isnull,
> >
> >      oldCtx = MemoryContextSwitchTo(insertCtx);
> >
> > +// elog(WARNING, "GinGetUseFastUpdate = %i", GinGetUseFastUpdate(index));
> > +
> >
> >      if (GinGetUseFastUpdate(index))
> >      {
> >
> >          GinTupleCollector collector;
> >
> > diff --git a/src/backend/access/gin/ginutil.c
> > b/src/backend/access/gin/ginutil.c index 6d2d71b..d1fa3a0 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"
> >
> > @@ -28,6 +28,7 @@
> >
> >  #include "utils/builtins.h"
> >  #include "utils/index_selfuncs.h"
> >  #include "utils/typcache.h"
> >
> > +#include "utils/guc.h"
> >
> >  /*
> >
> > @@ -67,7 +68,6 @@ ginhandler(PG_FUNCTION_ARGS)
> >
> >      amroutine->amvacuumcleanup = ginvacuumcleanup;
> >      amroutine->amcanreturn = NULL;
> >      amroutine->amcostestimate = gincostestimate;
> >
> > -    amroutine->amoptions = ginoptions;
> >
> >      amroutine->amproperty = NULL;
> >      amroutine->ambuildphasename = NULL;
> >      amroutine->amvalidate = ginvalidate;
> >
> > @@ -82,6 +82,7 @@ ginhandler(PG_FUNCTION_ARGS)
> >
> >      amroutine->amestimateparallelscan = NULL;
> >      amroutine->aminitparallelscan = NULL;
> >      amroutine->amparallelrescan = NULL;
> >
> > +    amroutine->amreloptspecset = gingetreloptspecset;
> >
> >      PG_RETURN_POINTER(amroutine);
> >
> >  }
> >
> > @@ -604,6 +605,7 @@ ginExtractEntries(GinState *ginstate, OffsetNumber
> > attnum,>
> >      return entries;
> >
> >  }
> >
> > +/*
> >
> >  bytea *
> >  ginoptions(Datum reloptions, bool validate)
> >  {
> >
> > @@ -618,6 +620,7 @@ ginoptions(Datum reloptions, bool validate)
> >
> >
sizeof(GinOptions),
> >
tab, lengthof(tab));
> >
> >  }
> >
> > +*/
> >
> >  /*
> >
> >   * Fetch index's statistical data into *stats
> >
> > @@ -705,3 +708,31 @@ ginUpdateStats(Relation index, const GinStatsData
> > *stats, bool is_build)>
> >      END_CRIT_SECTION();
> >
> >  }
> >
> > +
> > +static options_spec_set *gin_relopt_specset = NULL;
> > +
> > +void *
> > +gingetreloptspecset(void)
> > +{
> > +    if (gin_relopt_specset)
> > +        return gin_relopt_specset;
> > +
> > +    gin_relopt_specset = allocateOptionsSpecSet(NULL,
> > +
        sizeof(GinOptions), 2);
> > +
> > +    optionsSpecSetAddBool(gin_relopt_specset, "fastupdate",
> > +                        "Enables \"fast update\"
feature for this GIN index",
> > +
AccessExclusiveLock,
> > +                              0,
> > +                              offsetof(GinOptions,
useFastUpdate),
> > +
GIN_DEFAULT_USE_FASTUPDATE);
> > +
> > +    optionsSpecSetAddInt(gin_relopt_specset, "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_specset;
> > +}
> > diff --git a/src/backend/access/gist/gist.c
> > b/src/backend/access/gist/gist.c index 0683f42..cbbc6a5 100644
> > --- a/src/backend/access/gist/gist.c
> > +++ b/src/backend/access/gist/gist.c
> > @@ -88,7 +88,6 @@ gisthandler(PG_FUNCTION_ARGS)
> >
> >      amroutine->amvacuumcleanup = gistvacuumcleanup;
> >      amroutine->amcanreturn = gistcanreturn;
> >      amroutine->amcostestimate = gistcostestimate;
> >
> > -    amroutine->amoptions = gistoptions;
> >
> >      amroutine->amproperty = gistproperty;
> >      amroutine->ambuildphasename = NULL;
> >      amroutine->amvalidate = gistvalidate;
> >
> > @@ -103,6 +102,7 @@ gisthandler(PG_FUNCTION_ARGS)
> >
> >      amroutine->amestimateparallelscan = NULL;
> >      amroutine->aminitparallelscan = NULL;
> >      amroutine->amparallelrescan = NULL;
> >
> > +    amroutine->amreloptspecset = gistgetreloptspecset;
> >
> >      PG_RETURN_POINTER(amroutine);
> >
> >  }
> >
> > diff --git a/src/backend/access/gist/gistbuild.c
> > b/src/backend/access/gist/gistbuild.c index baad28c..931d249 100644
> > --- a/src/backend/access/gist/gistbuild.c
> > +++ b/src/backend/access/gist/gistbuild.c
> > @@ -215,6 +215,7 @@ gistbuild(Relation heap, Relation index, IndexInfo
> > *indexInfo)>
> >              buildstate.buildMode = GIST_BUFFERING_DISABLED;
> >
> >          else                    /* must be "auto"
*/
> >
> >              buildstate.buildMode = GIST_BUFFERING_AUTO;
> >
> > +//elog(WARNING, "biffering_mode = %i", options->buffering_mode);
> >
> >      }
> >      else
> >      {
> >
> > diff --git a/src/backend/access/gist/gistutil.c
> > b/src/backend/access/gist/gistutil.c index 43ba03b..0391915 100644
> > --- a/src/backend/access/gist/gistutil.c
> > +++ b/src/backend/access/gist/gistutil.c
> > @@ -17,7 +17,7 @@
> >
> >  #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"
> >
> > @@ -916,20 +916,6 @@ gistPageRecyclable(Page page)
> >
> >      return false;
> >
> >  }
> >
> > -bytea *
> > -gistoptions(Datum reloptions, bool validate)
> > -{
> > -    static const relopt_parse_elt tab[] = {
> > -        {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions,
fillfactor)},
> > -        {"buffering", RELOPT_TYPE_ENUM, offsetof(GiSTOptions,
buffering_mode)}
> > -    };
> > -
> > -    return (bytea *) build_reloptions(reloptions, validate,
> > -
RELOPT_KIND_GIST,
> > -
sizeof(GiSTOptions),
> > -
tab, lengthof(tab));
> > -}
> > -
> >
> >  /*
> >
> >   *    gistproperty() -- Check boolean properties of indexes.
> >   *
> >
> > @@ -1064,3 +1050,42 @@ gistGetFakeLSN(Relation rel)
> >
> >          return GetFakeLSNForUnloggedRel();
> >
> >      }
> >
> >  }
> >
> > +
> > +/* values from GistOptBufferingMode */
> > +opt_enum_elt_def gistBufferingOptValues[] =
> > +{
> > +    {"auto", GIST_OPTION_BUFFERING_AUTO},
> > +    {"on", GIST_OPTION_BUFFERING_ON},
> > +    {"off", GIST_OPTION_BUFFERING_OFF},
> > +    {(const char *) NULL}        /* list terminator */
> > +};
> > +
> > +static options_spec_set *gist_relopt_specset = NULL;
> > +
> > +void *
> > +gistgetreloptspecset(void)
> > +{
> > +    if (gist_relopt_specset)
> > +        return gist_relopt_specset;
> > +
> > +    gist_relopt_specset = allocateOptionsSpecSet(NULL,
> > +
         sizeof(GiSTOptions), 2);
> > +
> > +    optionsSpecSetAddInt(gist_relopt_specset, "fillfactor",
> > +                        "Packs gist index pages only
to this percentage",
> > +                             NoLock,        /*
No ALTER, no lock */
> > +                             0,
> > +                             offsetof(GiSTOptions,
fillfactor),
> > +
GIST_DEFAULT_FILLFACTOR,
> > +                             GIST_MIN_FILLFACTOR,
100);
> > +
> > +    optionsSpecSetAddEnum(gist_relopt_specset, "buffering",
> > +                           "Enables buffering build
for this GiST index",
> > +                              NoLock,        /*
No ALTER, no lock */
> > +                              0,
> > +
offsetof(GiSTOptions, buffering_mode),
> > +
gistBufferingOptValues,
> > +
GIST_OPTION_BUFFERING_AUTO,
> > +                              gettext_noop("Valid
values are \"on\", \"off\", and
> > \"auto\"."));
> > +    return gist_relopt_specset;
> > +}
> > diff --git a/src/backend/access/hash/hash.c
> > b/src/backend/access/hash/hash.c index eb38104..8dc4ca7 100644
> > --- a/src/backend/access/hash/hash.c
> > +++ b/src/backend/access/hash/hash.c
> > @@ -85,7 +85,6 @@ hashhandler(PG_FUNCTION_ARGS)
> >
> >      amroutine->amvacuumcleanup = hashvacuumcleanup;
> >      amroutine->amcanreturn = NULL;
> >      amroutine->amcostestimate = hashcostestimate;
> >
> > -    amroutine->amoptions = hashoptions;
> >
> >      amroutine->amproperty = NULL;
> >      amroutine->ambuildphasename = NULL;
> >      amroutine->amvalidate = hashvalidate;
> >
> > @@ -100,6 +99,7 @@ hashhandler(PG_FUNCTION_ARGS)
> >
> >      amroutine->amestimateparallelscan = NULL;
> >      amroutine->aminitparallelscan = NULL;
> >      amroutine->amparallelrescan = NULL;
> >
> > +    amroutine->amreloptspecset = hashgetreloptspecset;
> >
> >      PG_RETURN_POINTER(amroutine);
> >
> >  }
> >
> > diff --git a/src/backend/access/hash/hashpage.c
> > b/src/backend/access/hash/hashpage.c index 159646c..38f64ef 100644
> > --- a/src/backend/access/hash/hashpage.c
> > +++ b/src/backend/access/hash/hashpage.c
> > @@ -359,6 +359,8 @@ _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 */
> >
> > +//elog(WARNING, "fillfactor = %i", HashGetFillFactor(rel));
> > +
> >
> >      ffactor = HashGetTargetPageUsage(rel) / item_width;
> >      /* keep to a sane range */
> >      if (ffactor < 10)
> >
> > diff --git a/src/backend/access/hash/hashutil.c
> > b/src/backend/access/hash/hashutil.c index 5198728..826beab 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 "port/pg_bitutils.h"
> >  #include "storage/buf_internals.h"
> >
> > @@ -272,19 +272,6 @@ _hash_checkpage(Relation rel, Buffer buf, int flags)
> >
> >      }
> >
> >  }
> >
> > -bytea *
> > -hashoptions(Datum reloptions, bool validate)
> > -{
> > -    static const relopt_parse_elt tab[] = {
> > -        {"fillfactor", RELOPT_TYPE_INT, offsetof(HashOptions,
fillfactor)},
> > -    };
> > -
> > -    return (bytea *) build_reloptions(reloptions, validate,
> > -
RELOPT_KIND_HASH,
> > -
sizeof(HashOptions),
> > -
tab, lengthof(tab));
> > -}
> > -
> >
> >  /*
> >
> >   * _hash_get_indextuple_hashkey - get the hash index tuple's hash key
> >   value
> >   */
> >
> > @@ -620,3 +607,24 @@ _hash_kill_items(IndexScanDesc scan)
> >
> >      else
> >
> >          _hash_relbuf(rel, buf);
> >
> >  }
> >
> > +
> > +static options_spec_set *hash_relopt_specset = NULL;
> > +
> > +void *
> > +hashgetreloptspecset(void)
> > +{
> > +    if (hash_relopt_specset)
> > +        return hash_relopt_specset;
> > +
> > +    hash_relopt_specset = allocateOptionsSpecSet(NULL,
> > +
      sizeof(HashOptions), 1);
> > +    optionsSpecSetAddInt(hash_relopt_specset, "fillfactor",
> > +                        "Packs hash index pages only
to this percentage",
> > +                             NoLock,        /*
No ALTER -- no lock */
> > +                             0,
> > +                             offsetof(HashOptions,
fillfactor),
> > +
HASH_DEFAULT_FILLFACTOR,
> > +                             HASH_MIN_FILLFACTOR,
100);
> > +
> > +    return hash_relopt_specset;
> > +}
> > diff --git a/src/backend/access/nbtree/nbtinsert.c
> > b/src/backend/access/nbtree/nbtinsert.c index 7355e1d..f7b117e 100644
> > --- a/src/backend/access/nbtree/nbtinsert.c
> > +++ b/src/backend/access/nbtree/nbtinsert.c
> > @@ -2745,6 +2745,8 @@ _bt_delete_or_dedup_one_page(Relation rel, Relation
> > heapRel,>
> >          _bt_bottomupdel_pass(rel, buffer, heapRel, insertstate-
>itemsz))
> >          return;
> >
> > +// elog(WARNING, "Deduplicate_items = %i", BTGetDeduplicateItems(rel));
> > +
> >
> >      /* Perform deduplication pass (when enabled and index-is-
allequalimage)
> >      */
> >      if (BTGetDeduplicateItems(rel) && itup_key->allequalimage)
> >
> >          _bt_dedup_pass(rel, buffer, heapRel, insertstate->itup,
> >
> > diff --git a/src/backend/access/nbtree/nbtree.c
> > b/src/backend/access/nbtree/nbtree.c index 40ad095..f171c54 100644
> > --- a/src/backend/access/nbtree/nbtree.c
> > +++ b/src/backend/access/nbtree/nbtree.c
> > @@ -22,6 +22,7 @@
> >
> >  #include "access/nbtxlog.h"
> >  #include "access/relscan.h"
> >  #include "access/xlog.h"
> >
> > +#include "access/options.h"
> >
> >  #include "commands/progress.h"
> >  #include "commands/vacuum.h"
> >  #include "miscadmin.h"
> >
> > @@ -124,7 +125,6 @@ bthandler(PG_FUNCTION_ARGS)
> >
> >      amroutine->amvacuumcleanup = btvacuumcleanup;
> >      amroutine->amcanreturn = btcanreturn;
> >      amroutine->amcostestimate = btcostestimate;
> >
> > -    amroutine->amoptions = btoptions;
> >
> >      amroutine->amproperty = btproperty;
> >      amroutine->ambuildphasename = btbuildphasename;
> >      amroutine->amvalidate = btvalidate;
> >
> > @@ -139,6 +139,7 @@ bthandler(PG_FUNCTION_ARGS)
> >
> >      amroutine->amestimateparallelscan = btestimateparallelscan;
> >      amroutine->aminitparallelscan = btinitparallelscan;
> >      amroutine->amparallelrescan = btparallelrescan;
> >
> > +    amroutine->amreloptspecset = btgetreloptspecset;
> >
> >      PG_RETURN_POINTER(amroutine);
> >
> >  }
> >
> > @@ -1418,3 +1419,37 @@ btcanreturn(Relation index, int attno)
> >
> >  {
> >
> >      return true;
> >
> >  }
> >
> > +
> > +static options_spec_set *bt_relopt_specset = NULL;
> > +
> > +void *
> > +btgetreloptspecset(void)
> > +{
> > +    if (bt_relopt_specset)
> > +        return bt_relopt_specset;
> > +
> > +    bt_relopt_specset = allocateOptionsSpecSet(NULL,
> > +
       sizeof(BTOptions), 3);
> > +
> > +    optionsSpecSetAddInt(
> > +        bt_relopt_specset, "fillfactor",
> > +        "Packs btree index pages only to this percentage",
> > +        ShareUpdateExclusiveLock, /* since it applies only to later
inserts */
> > +        0, offsetof(BTOptions, fillfactor),
> > +        BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
> > +    );
> > +    optionsSpecSetAddReal(
> > +        bt_relopt_specset, "vacuum_cleanup_index_scale_factor",
> > +        "Number of tuple inserts prior to index cleanup as a fraction
of
> > reltuples", +        ShareUpdateExclusiveLock,
> > +        0, offsetof(BTOptions,vacuum_cleanup_index_scale_factor),
> > +        -1, 0.0, 1e10
> > +    );
> > +    optionsSpecSetAddBool(
> > +        bt_relopt_specset, "deduplicate_items",
> > +        "Enables \"deduplicate items\" feature for this btree index",
> > +        ShareUpdateExclusiveLock, /* since it applies only to later
inserts */
> > +        0, offsetof(BTOptions,deduplicate_items), true
> > +    );
> > +    return bt_relopt_specset;
> > +}
> > diff --git a/src/backend/access/nbtree/nbtutils.c
> > b/src/backend/access/nbtree/nbtutils.c index c72b456..2588a30 100644
> > --- a/src/backend/access/nbtree/nbtutils.c
> > +++ b/src/backend/access/nbtree/nbtutils.c
> > @@ -18,7 +18,7 @@
> >
> >  #include <time.h>
> >
> >  #include "access/nbtree.h"
> >
> > -#include "access/reloptions.h"
> > +#include "storage/lock.h"
> >
> >  #include "access/relscan.h"
> >  #include "catalog/catalog.h"
> >  #include "commands/progress.h"
> >
> > @@ -2100,25 +2100,6 @@ BTreeShmemInit(void)
> >
> >          Assert(found);
> >
> >  }
> >
> > -bytea *
> > -btoptions(Datum reloptions, bool validate)
> > -{
> > -    static const relopt_parse_elt tab[] = {
> > -        {"fillfactor", RELOPT_TYPE_INT, offsetof(BTOptions,
fillfactor)},
> > -        {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
> > -        offsetof(BTOptions, vacuum_cleanup_index_scale_factor)},
> > -        {"deduplicate_items", RELOPT_TYPE_BOOL,
> > -        offsetof(BTOptions, deduplicate_items)}
> > -
> > -    };
> > -
> > -    return (bytea *) build_reloptions(reloptions, validate,
> > -
RELOPT_KIND_BTREE,
> > -
sizeof(BTOptions),
> > -
tab, lengthof(tab));
> > -
> > -}
> > -
> >
> >  /*
> >
> >   *    btproperty() -- Check boolean properties of indexes.
> >   *
> >
> > diff --git a/src/backend/access/spgist/spgutils.c
> > b/src/backend/access/spgist/spgutils.c index 03a9cd3..14429ad 100644
> > --- a/src/backend/access/spgist/spgutils.c
> > +++ b/src/backend/access/spgist/spgutils.c
> > @@ -17,7 +17,7 @@
> >
> >  #include "access/amvalidate.h"
> >  #include "access/htup_details.h"
> >
> > -#include "access/reloptions.h"
> > +#include "access/options.h"
> >
> >  #include "access/spgist_private.h"
> >  #include "access/toast_compression.h"
> >  #include "access/transam.h"
> >
> > @@ -72,7 +72,6 @@ spghandler(PG_FUNCTION_ARGS)
> >
> >      amroutine->amvacuumcleanup = spgvacuumcleanup;
> >      amroutine->amcanreturn = spgcanreturn;
> >      amroutine->amcostestimate = spgcostestimate;
> >
> > -    amroutine->amoptions = spgoptions;
> >
> >      amroutine->amproperty = spgproperty;
> >      amroutine->ambuildphasename = NULL;
> >      amroutine->amvalidate = spgvalidate;
> >
> > @@ -87,6 +86,7 @@ spghandler(PG_FUNCTION_ARGS)
> >
> >      amroutine->amestimateparallelscan = NULL;
> >      amroutine->aminitparallelscan = NULL;
> >      amroutine->amparallelrescan = NULL;
> >
> > +    amroutine->amreloptspecset = spggetreloptspecset;
> >
> >      PG_RETURN_POINTER(amroutine);
> >
> >  }
> >
> > @@ -550,6 +550,7 @@ 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.
> >       */
> >
> > +//elog(WARNING, "fillfactor = %i", SpGistGetFillFactor(index));
> >
> >      needSpace += SpGistGetTargetPageFreeSpace(index);
> >      needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
> >
> > @@ -721,23 +722,6 @@ SpGistInitMetapage(Page page)
> >
> >  }
> >
> >  /*
> >
> > - * reloptions processing for SPGiST
> > - */
> > -bytea *
> > -spgoptions(Datum reloptions, bool validate)
> > -{
> > -    static const relopt_parse_elt tab[] = {
> > -        {"fillfactor", RELOPT_TYPE_INT, offsetof(SpGistOptions,
fillfactor)},
> > -    };
> > -
> > -    return (bytea *) build_reloptions(reloptions, validate,
> > -
RELOPT_KIND_SPGIST,
> > -
sizeof(SpGistOptions),
> > -
tab, lengthof(tab));
> > -
> > -}
> > -
> > -/*
> >
> >   * Get the space needed to store a non-null datum of the indicated type
> >   * in an inner tuple (that is, as a prefix or node label).
> >   * Note the result is already rounded up to a MAXALIGN boundary.
> >
> > @@ -1336,3 +1320,25 @@ spgproperty(Oid index_oid, int attno,
> >
> >      return true;
> >
> >  }
> >
> > +
> > +static options_spec_set *spgist_relopt_specset = NULL;
> > +
> > +void *
> > +spggetreloptspecset(void)
> > +{
> > +    if (!spgist_relopt_specset)
> > +    {
> > +        spgist_relopt_specset = allocateOptionsSpecSet(NULL,
> > +
        sizeof(SpGistOptions), 1);
> > +
> > +        optionsSpecSetAddInt(spgist_relopt_specset, "fillfactor",
> > +                          "Packs spgist index pages
only to this percentage",
> > +
ShareUpdateExclusiveLock,        /* since it applies only
> > +
                         * to later inserts */
> > +                                 0,
> > +
offsetof(SpGistOptions, fillfactor),
> > +
SPGIST_DEFAULT_FILLFACTOR,
> > +
SPGIST_MIN_FILLFACTOR, 100);
> > +    }
> > +    return spgist_relopt_specset;
> > +}
> > diff --git a/src/backend/commands/createas.c
> > b/src/backend/commands/createas.c index 0982851..4f3dbb8 100644
> > --- a/src/backend/commands/createas.c
> > +++ b/src/backend/commands/createas.c
> > @@ -90,6 +90,7 @@ create_ctas_internal(List *attrList, IntoClause *into)
> >
> >      Datum        toast_options;
> >      static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
> >      ObjectAddress intoRelationAddr;
> >
> > +    List       *toastDefList;
> >
> >      /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED
VIEW
> >      */
> >      is_matview = (into->viewQuery != NULL);
> >
> > @@ -124,14 +125,12 @@ 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);
> > +    optionsDefListValdateNamespaces(create->options, validnsps);
> > +    toastDefList = optionsDefListFilterNamespaces(create->options,
"toast");
> >
> > +    toast_options = transformOptions(get_toast_relopt_spec_set(), (Datum)
0,
> > +
toastDefList, 0);
> >
> >      NewRelationCreateToastTable(intoRelationAddr.objectId,
toast_options);
> >
> >      /* Create the "view" part of a materialized view. */
> >
> > diff --git a/src/backend/commands/foreigncmds.c
> > b/src/backend/commands/foreigncmds.c index 146fa57..758ca34 100644
> > --- a/src/backend/commands/foreigncmds.c
> > +++ b/src/backend/commands/foreigncmds.c
> > @@ -112,7 +112,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 c14ca27..96d465a 100644
> > --- a/src/backend/commands/indexcmds.c
> > +++ b/src/backend/commands/indexcmds.c
> > @@ -19,6 +19,7 @@
> >
> >  #include "access/heapam.h"
> >  #include "access/htup_details.h"
> >  #include "access/reloptions.h"
> >
> > +#include "access/options.h"
> >
> >  #include "access/sysattr.h"
> >  #include "access/tableam.h"
> >  #include "access/xact.h"
> >
> > @@ -531,7 +532,7 @@ DefineIndex(Oid relationId,
> >
> >      Form_pg_am    accessMethodForm;
> >      IndexAmRoutine *amRoutine;
> >      bool        amcanorder;
> >
> > -    amoptions_function amoptions;
> > +    amreloptspecset_function amreloptspecsetfn;
> >
> >      bool        partitioned;
> >      bool        safe_index;
> >      Datum        reloptions;
> >
> > @@ -837,7 +838,7 @@ DefineIndex(Oid relationId,
> >
> >                          accessMethodName)));
> >
> >      amcanorder = amRoutine->amcanorder;
> >
> > -    amoptions = amRoutine->amoptions;
> > +    amreloptspecsetfn = amRoutine->amreloptspecset;
> >
> >      pfree(amRoutine);
> >      ReleaseSysCache(tuple);
> >
> > @@ -851,10 +852,19 @@ 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 (amreloptspecsetfn)
> > +    {
> > +        reloptions = transformOptions(amreloptspecsetfn(),
> > +
(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.
> >
> > @@ -1986,8 +1996,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
> >
> >                      palloc0(sizeof(Datum) * indexInfo-
>ii_NumIndexAttrs);
> >
> >              indexInfo->ii_OpclassOptions[attn] =
> >
> > -                transformRelOptions((Datum) 0, attribute-
>opclassopts,
> > -
NULL, NULL, false, false);
> > +                optionsDefListToTextArray(attribute-
>opclassopts);
> >
> >          }
> >
> >          attn++;
> >
> > diff --git a/src/backend/commands/tablecmds.c
> > b/src/backend/commands/tablecmds.c index 1c2ebe1..7f3004f 100644
> > --- a/src/backend/commands/tablecmds.c
> > +++ b/src/backend/commands/tablecmds.c
> > @@ -20,6 +20,7 @@
> >
> >  #include "access/heapam_xlog.h"
> >  #include "access/multixact.h"
> >  #include "access/reloptions.h"
> >
> > +#include "access/options.h"
> >
> >  #include "access/relscan.h"
> >  #include "access/sysattr.h"
> >  #include "access/tableam.h"
> >
> > @@ -641,7 +642,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid
> > ownerId,>
> >      ListCell   *listptr;
> >      AttrNumber    attnum;
> >      bool        partitioned;
> >
> > -    static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
> >
> >      Oid            ofTypeId;
> >      ObjectAddress address;
> >      LOCKMODE    parentLockmode;
> >
> > @@ -789,19 +789,37 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid
> > ownerId,>
> >      /*
> >
> >       * Parse and validate reloptions, if any.
> >       */
> >
> > -    reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
> > validnsps, -
 true, false);
> >
> >      switch (relkind)
> >      {
> >
> >          case RELKIND_VIEW:
> > -            (void) view_reloptions(reloptions, true);
> > +            reloptions = transformOptions(
> > +
get_view_relopt_spec_set(),
> > +
(Datum) 0, stmt->options, 0);
> >
> >              break;
> >
> >          case RELKIND_PARTITIONED_TABLE:
> > -            (void) partitioned_table_reloptions(reloptions,
true);
> > +        {
> > +            /* If it is not all listed above, then it if heap */
> > +            char       *namespaces[] = HEAP_RELOPT_NAMESPACES;
> > +            List       *heapDefList;
> > +
> > +            optionsDefListValdateNamespaces(stmt->options,
namespaces);
> > +            heapDefList = optionsDefListFilterNamespaces(stmt-
>options, NULL);
> > +            reloptions =
transformOptions(get_partitioned_relopt_spec_set(),
> > +
(Datum) 0, heapDefList, 0);
> >
> >              break;
> >
> > +        }
> >
> >          default:
> > -            (void) heap_reloptions(relkind, reloptions, true);
> > +        {
> > +            /* If it is not all listed above, 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_spec_set(),
> > +
(Datum) 0, heapDefList, 0);
> > +        }
> >
> >      }
> >
> >      if (stmt->ofTypename)
> >
> > @@ -4022,7 +4040,7 @@ void
> >
> >  AlterTableInternal(Oid relid, List *cmds, bool recurse)
> >  {
> >
> >      Relation    rel;
> >
> > -    LOCKMODE    lockmode = AlterTableGetLockLevel(cmds);
> > +    LOCKMODE    lockmode = AlterTableGetLockLevel(relid, cmds);
> >
> >      rel = relation_open(relid, lockmode);
> >
> > @@ -4064,7 +4082,7 @@ 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.
> >
> > @@ -4285,9 +4303,13 @@ AlterTableGetLockLevel(List *cmds)
> >
> >                                       *
getTables() */
> >
> >              case AT_ResetRelOptions:    /* Uses MVCC in
getIndexes() and
> >
> >
 * getTables() */
> >
> > -                cmd_lockmode =
AlterTableGetRelOptionsLockLevel((List *) cmd->def);
> > -                break;
> > -
> > +                {
> > +                    Relation rel = relation_open(relid,
NoLock);  // FIXME I am not sure
> > how wise it is +                    cmd_lockmode =
AlterTableGetRelOptionsLockLevel(rel,
> > +
            castNode(List, cmd->def));
> > +                    relation_close(rel,NoLock);
> > +                    break;
> > +                }
> >
> >              case AT_AttachPartition:
> >                  cmd_lockmode = ShareUpdateExclusiveLock;
> >                  break;
> >
> > @@ -8062,11 +8084,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_spec_set(),
> > +                                  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));
> >
> > @@ -13704,7 +13726,8 @@ 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;
> > +    List       *toastDefList;
> > +    options_parse_mode parse_mode;
> >
> >      if (defList == NIL && operation != AT_ReplaceRelOptions)
> >
> >          return;                    /* nothing to do
*/
> >
> > @@ -13734,27 +13757,68 @@ ATExecSetRelOptions(Relation rel, List *defList,
> > AlterTableType operation,>
> >      }
> >
> >      /* 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_TOASTVALUE: // FIXME why it is here???
> >
> >          case RELKIND_MATVIEW:
> > -            (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_spec_set(),
> > +
      isnull ? (Datum) 0 : datum,
> > +
      heapDefList, parse_mode);
> > +            }
> >
> >              break;
> >
> > +
> >
> >          case RELKIND_PARTITIONED_TABLE:
> > -            (void) partitioned_table_reloptions(newOptions,
true);
> > -            break;
> > +            {
> > +                char       *namespaces[] =
HEAP_RELOPT_NAMESPACES;
> > +                List       *heapDefList;
> > +
> > +                optionsDefListValdateNamespaces(defList,
namespaces);
> > +                heapDefList = optionsDefListFilterNamespaces(
> > +
                     defList, NULL);
> > +                newOptions =
transformOptions(get_partitioned_relopt_spec_set(),
> > +
      isnull ? (Datum) 0 : datum,
> > +
      heapDefList, parse_mode);
> > +                break;
> > +            }
> >
> >          case RELKIND_VIEW:
> > -            (void) view_reloptions(newOptions, true);
> > -            break;
> > +            {
> > +
> > +                newOptions = transformOptions(
> > +
get_view_relopt_spec_set(),
> > +
datum, defList, parse_mode);
> > +                break;
> > +            }
> >
> >          case RELKIND_INDEX:
> >
> >          case RELKIND_PARTITIONED_INDEX:
> > -            (void) index_reloptions(rel->rd_indam->amoptions,
newOptions, true);
> > +            if (! rel->rd_indam->amreloptspecset)
> > +            {
> > +                ereport(ERROR,
> > +
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
> > +                         errmsg("index %s does not
support options",
> > +
RelationGetRelationName(rel))));
> > +                break;
> > +            }
> > +            parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;
> > +            if (operation == AT_ResetRelOptions)
> > +                parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;
> > +            newOptions = transformOptions(
> > +                                    rel-
>rd_indam->amreloptspecset(),
> > +
    isnull ? (Datum) 0 : datum,
> > +
    defList, parse_mode);
> >
> >              break;
> >
> >          default:
> >              ereport(ERROR,
> >
> > @@ -13769,7 +13833,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;
> >
> > @@ -13853,11 +13917,15 @@ ATExecSetRelOptions(Relation rel, List *defList,
> > AlterTableType operation,>
> >
&isnull);
> >
> >          }
> >
> > -        newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
> > -
 defList, "toast", validnsps, false,
> > -
 operation == AT_ResetRelOptions);
> > +        parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;
> > +        if (operation == AT_ResetRelOptions)
> > +            parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;
> > +
> > +        toastDefList = optionsDefListFilterNamespaces(defList,
"toast");
> >
> > -        (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions,
true);
> > +        newOptions = transformOptions(get_toast_relopt_spec_set(),
> > +
isnull ? (Datum) 0 : datum,
> > +
toastDefList, parse_mode);
> >
> >          memset(repl_val, 0, sizeof(repl_val));
> >          memset(repl_null, false, sizeof(repl_null));
> >
> > diff --git a/src/backend/commands/tablespace.c
> > b/src/backend/commands/tablespace.c index 4b96eec..912699b 100644
> > --- a/src/backend/commands/tablespace.c
> > +++ b/src/backend/commands/tablespace.c
> > @@ -345,10 +345,9 @@ 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_spec_set(),
> > +
        (Datum) 0, stmt->options, 0);
> > +
> >
> >      if (newOptions != (Datum) 0)
> >
> >          values[Anum_pg_tablespace_spcoptions - 1] = newOptions;
> >
> >      else
> >
> > @@ -1053,10 +1052,11 @@ 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_spec_set(),
> > +                                  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 5564dc3..0370be7 100644
> > --- a/src/backend/foreign/foreign.c
> > +++ b/src/backend/foreign/foreign.c
> > @@ -78,7 +78,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
> >
> >      if (isnull)
> >
> >          fdw->options = NIL;
> >
> >      else
> >
> > -        fdw->options = untransformRelOptions(datum);
> > +        fdw->options = optionsTextArrayToDefList(datum);
> >
> >      ReleaseSysCache(tp);
> >
> > @@ -165,7 +165,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
> >
> >      if (isnull)
> >
> >          server->options = NIL;
> >
> >      else
> >
> > -        server->options = untransformRelOptions(datum);
> > +        server->options = optionsTextArrayToDefList(datum);
> >
> >      ReleaseSysCache(tp);
> >
> > @@ -233,7 +233,7 @@ GetUserMapping(Oid userid, Oid serverid)
> >
> >      if (isnull)
> >
> >          um->options = NIL;
> >
> >      else
> >
> > -        um->options = untransformRelOptions(datum);
> > +        um->options = optionsTextArrayToDefList(datum);
> >
> >      ReleaseSysCache(tp);
> >
> > @@ -270,7 +270,7 @@ GetForeignTable(Oid relid)
> >
> >      if (isnull)
> >
> >          ft->options = NIL;
> >
> >      else
> >
> > -        ft->options = untransformRelOptions(datum);
> > +        ft->options = optionsTextArrayToDefList(datum);
> >
> >      ReleaseSysCache(tp);
> >
> > @@ -303,7 +303,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
> >
> >      if (isnull)
> >
> >          options = NIL;
> >
> >      else
> >
> > -        options = untransformRelOptions(datum);
> > +        options = optionsTextArrayToDefList(datum);
> >
> >      ReleaseSysCache(tp);
> >
> > @@ -572,7 +572,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;
> >
> >  }
> >
> > @@ -643,7 +643,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/parser/parse_utilcmd.c
> > b/src/backend/parser/parse_utilcmd.c index 313d7b6..1fe41b4 100644
> > --- a/src/backend/parser/parse_utilcmd.c
> > +++ b/src/backend/parser/parse_utilcmd.c
> > @@ -1757,7 +1757,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation
> > source_idx,>
> >          /* Add the operator class name, if non-default */
> >          iparam->opclass = get_opclass(indclass->values[keyno],
keycoltype);
> >          iparam->opclassopts =
> >
> > -            untransformRelOptions(get_attoptions(source_relid,
keyno + 1));
> > +
optionsTextArrayToDefList(get_attoptions(source_relid, keyno + 1));
> >
> >          iparam->ordering = SORTBY_DEFAULT;
> >          iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
> >
> > @@ -1821,7 +1821,7 @@ generateClonedIndexStmt(RangeVar *heapRel, 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/tcop/utility.c b/src/backend/tcop/utility.c
> > index bf085aa..d12ab1a 100644
> > --- a/src/backend/tcop/utility.c
> > +++ b/src/backend/tcop/utility.c
> > @@ -1155,6 +1155,7 @@ ProcessUtilitySlow(ParseState *pstate,
> >
> >                              CreateStmt *cstmt =
(CreateStmt *) stmt;
> >                              Datum
toast_options;
> >                              static char
*validnsps[] = HEAP_RELOPT_NAMESPACES;
> >
> > +                            List
*toastDefList;
> >
> >                              /* Remember
transformed RangeVar for LIKE */
> >                              table_rv = cstmt-
>relation;
> >
> > @@ -1178,15 +1179,17 @@ ProcessUtilitySlow(ParseState *pstate,
> >
> >                               * parse and
validate reloptions for the toast
> >                               * table
> >                               */
> >
> > -                            toast_options =
transformRelOptions((Datum) 0,
> > -
                        cstmt->options,
> > -
                        "toast",
> > -
                        validnsps,
> > -
                        true,
> > -
                        false);
> > -                            (void)
heap_reloptions(RELKIND_TOASTVALUE,
> > -
           toast_options,
> > -
           true);
> > +
> > +
optionsDefListValdateNamespaces(
> > +
      ((CreateStmt *) stmt)->options,
> > +
                    validnsps);
> > +
> > +                            toastDefList =
optionsDefListFilterNamespaces(
> > +
((CreateStmt *) stmt)->options, "toast");
> > +
> > +                            toast_options =
transformOptions(
> > +
get_toast_relopt_spec_set(), (Datum) 0,
> > +
                     toastDefList, 0);
> >
> >
NewRelationCreateToastTable(address.objectId,
> >
> >
                toast_options);
> >
> > @@ -1295,9 +1298,12 @@ 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); // FIXME!
> > +                    if (OidIsValid(relid))
> > +                    {
> > +                        lockmode =
AlterTableGetLockLevel(relid, atstmt->cmds);
> > +                        relid =
AlterTableLookupRelation(atstmt, lockmode);
> > +                    }
> >
> >                      if (OidIsValid(relid))
> >                      {
> >
> >                          AlterTableUtilityContext
atcontext;
> >
> > diff --git a/src/backend/utils/cache/attoptcache.c
> > b/src/backend/utils/cache/attoptcache.c index 72d89cb..f651129 100644
> > --- a/src/backend/utils/cache/attoptcache.c
> > +++ b/src/backend/utils/cache/attoptcache.c
> > @@ -16,6 +16,7 @@
> >
> >   */
> >
> >  #include "postgres.h"
> >
> > +#include "access/options.h"
> >
> >  #include "access/reloptions.h"
> >  #include "utils/attoptcache.h"
> >  #include "utils/catcache.h"
> >
> > @@ -148,7 +149,8 @@ get_attribute_options(Oid attrelid, int attnum)
> >
> >                  opts = NULL;
> >
> >              else
> >              {
> >
> > -                bytea       *bytea_opts =
attribute_reloptions(datum, false);
> > +                bytea   *bytea_opts =
optionsTextArrayToBytea(
> > +
get_attribute_options_spec_set(), datum, 0);
> >
> >                  opts =
MemoryContextAlloc(CacheMemoryContext,
> >
> >
  VARSIZE(bytea_opts));
> >
> > diff --git a/src/backend/utils/cache/relcache.c
> > b/src/backend/utils/cache/relcache.c index 13d9994..f22c2d9 100644
> > --- a/src/backend/utils/cache/relcache.c
> > +++ b/src/backend/utils/cache/relcache.c
> > @@ -441,7 +441,7 @@ static void
> >
> >  RelationParseRelOptions(Relation relation, HeapTuple tuple)
> >  {
> >
> >      bytea       *options;
> >
> > -    amoptions_function amoptsfn;
> > +    amreloptspecset_function amoptspecsetfn;
> >
> >      relation->rd_options = NULL;
> >
> > @@ -456,11 +456,11 @@ RelationParseRelOptions(Relation relation, HeapTuple
> > tuple)>
> >          case RELKIND_VIEW:
> >          case RELKIND_MATVIEW:
> >
> >          case RELKIND_PARTITIONED_TABLE:
> > -            amoptsfn = NULL;
> > +            amoptspecsetfn = NULL;
> >
> >              break;
> >
> >          case RELKIND_INDEX:
> >
> >          case RELKIND_PARTITIONED_INDEX:
> > -            amoptsfn = relation->rd_indam->amoptions;
> > +            amoptspecsetfn = relation->rd_indam->amreloptspecset;
> >
> >              break;
> >
> >          default:
> >              return;
> >
> > @@ -471,7 +471,7 @@ RelationParseRelOptions(Relation relation, HeapTuple
> > tuple)>
> >       * we might not have any other for pg_class yet (consider executing
this
> >       * code for pg_class itself)
> >       */
> >
> > -    options = extractRelOptions(tuple, GetPgClassDescriptor(),
amoptsfn);
> > +    options = extractRelOptions(tuple, GetPgClassDescriptor(),
> > amoptspecsetfn);>
> >      /*
> >
> >       * 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 5870f43..87f2fa5 100644
> > --- a/src/backend/utils/cache/spccache.c
> > +++ b/src/backend/utils/cache/spccache.c
> > @@ -148,7 +148,8 @@ get_tablespace(Oid spcid)
> >
> >              opts = NULL;
> >
> >          else
> >          {
> >
> > -            bytea       *bytea_opts =
tablespace_reloptions(datum, false);
> > +            bytea *bytea_opts  = optionsTextArrayToBytea(
> > +
get_tablespace_options_spec_set(), datum, 0);
> >
> >              opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
> >              memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
> >
> > diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
> > index d357ebb..b8fb6b9 100644
> > --- a/src/include/access/amapi.h
> > +++ b/src/include/access/amapi.h
> > @@ -136,10 +136,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,
> >
> > @@ -186,6 +182,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc
> > scan);>
> >  /* restore marked scan position */
> >  typedef void (*amrestrpos_function) (IndexScanDesc scan);
> >
> > +/* get catalog of reloptions definitions */
> > +typedef void *(*amreloptspecset_function) ();
> > +
> >
> >  /*
> >
> >   * Callback function signatures - for parallel index scans.
> >   */
> >
> > @@ -263,7 +262,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 */
> >      ambuildphasename_function ambuildphasename; /* can be NULL */
> >      amvalidate_function amvalidate;
> >
> > @@ -275,6 +273,7 @@ typedef struct IndexAmRoutine
> >
> >      amendscan_function amendscan;
> >      ammarkpos_function ammarkpos;    /* can be NULL */
> >      amrestrpos_function amrestrpos; /* can be NULL */
> >
> > +    amreloptspecset_function amreloptspecset; /* 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 4e2be13..25b3456 100644
> > --- a/src/include/access/brin.h
> > +++ b/src/include/access/brin.h
> > @@ -36,6 +36,8 @@ typedef struct BrinStatsData
> >
> >  #define BRIN_DEFAULT_PAGES_PER_RANGE    128
> >
> > +#define BRIN_MIN_PAGES_PER_RANGE        1
> > +#define BRIN_MAX_PAGES_PER_RANGE        131072
> >
> >  #define BrinGetPagesPerRange(relation) \
> >
> >      (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
> >
> >                   relation->rd_rel->relam == BRIN_AM_OID), \
> >
> > diff --git a/src/include/access/brin_internal.h
> > b/src/include/access/brin_internal.h index 79440eb..a798a96 100644
> > --- a/src/include/access/brin_internal.h
> > +++ b/src/include/access/brin_internal.h
> > @@ -14,6 +14,7 @@
> >
> >  #include "access/amapi.h"
> >  #include "storage/bufpage.h"
> >  #include "utils/typcache.h"
> >
> > +#include "access/options.h"
> >
> >  /*
> >
> > @@ -108,6 +109,7 @@ extern IndexBulkDeleteResult
> > *brinbulkdelete(IndexVacuumInfo *info,>
> >  extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
> >
> >
        IndexBulkDeleteResult *stats);
> >
> >  extern bytea *brinoptions(Datum reloptions, bool validate);
> >
> > +extern void * bringetreloptspecset (void);
> >
> >  /* 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 670a40b..2b7c25c 100644
> > --- a/src/include/access/gin_private.h
> > +++ b/src/include/access/gin_private.h
> > @@ -108,6 +108,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 *gingetreloptspecset(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 553d364..015b75a 100644
> > --- a/src/include/access/gist_private.h
> > +++ b/src/include/access/gist_private.h
> > @@ -22,6 +22,7 @@
> >
> >  #include "storage/buffile.h"
> >  #include "utils/hsearch.h"
> >  #include "access/genam.h"
> >
> > +#include "access/reloptions.h" //FIXME! should be replaced with options.h
> > finally>
> >  /*
> >
> >   * Maximum number of "halves" a page can be split into in one operation.
> >
> > @@ -388,6 +389,7 @@ typedef enum GistOptBufferingMode
> >
> >      GIST_OPTION_BUFFERING_OFF
> >
> >  } GistOptBufferingMode;
> >
> > +
> >
> >  /*
> >
> >   * Storage type for GiST's reloptions
> >   */
> >
> > @@ -478,7 +480,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
> >
> >  #define GIST_MIN_FILLFACTOR            10
> >  #define GIST_DEFAULT_FILLFACTOR        90
> >
> > -extern bytea *gistoptions(Datum reloptions, bool validate);
> > +extern void *gistgetreloptspecset(void);
> >
> >  extern bool gistproperty(Oid index_oid, int attno,
> >
> >                           IndexAMProperty prop, const
char *propname,
> >                           bool *res, bool *isnull);
> >
> > diff --git a/src/include/access/hash.h b/src/include/access/hash.h
> > index 1cce865..91922ef 100644
> > --- a/src/include/access/hash.h
> > +++ b/src/include/access/hash.h
> > @@ -378,7 +378,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 void hashadjustmembers(Oid opfamilyoid,
> >
> >                                Oid opclassoid,
> >
> > @@ -470,6 +469,7 @@ extern BlockNumber
> > _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu>
> >  extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket
> >  old_bucket,>
> >
         uint32 lowmask, uint32 maxbucket);
> >
> >  extern void _hash_kill_items(IndexScanDesc scan);
> >
> > +extern void *hashgetreloptspecset(void);
> >
> >  /* hash.c */
> >  extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
> >
> > diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
> > index 30a216e..1fcb5f5 100644
> > --- a/src/include/access/nbtree.h
> > +++ b/src/include/access/nbtree.h
> > @@ -1252,7 +1252,7 @@ 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 void * btgetreloptspecset (void);
> >
> >  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..34e2917
> > --- /dev/null
> > +++ b/src/include/access/options.h
> > @@ -0,0 +1,245 @@
> > +/*-----------------------------------------------------------------------
> > -- + *
> > + * 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"
> > +
> > +
> > +/* supported option types */
> > +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_spec_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_spec_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;
> > +
> > +
> > +
> > +/*
> > + * opt_enum_elt_def -- One member of the array of acceptable values
> > + * of an enum reloption.
> > + */
> > +typedef struct opt_enum_elt_def
> > +{
> > +    const char *string_val;
> > +    int            symbol_val;
> > +} opt_enum_elt_def;
> > +
> > +
> > +/* generic structure to store Option Spec information */
> > +typedef struct option_spec_basic
> > +{
> > +    const char *name;            /* must be first (used as list
termination
> > +                                 * marker) */
> > +    const char *desc;
> > +    LOCKMODE    lockmode;
> > +    option_spec_flags flags;
> > +    option_type type;
> > +    int            struct_offset;    /* offset of the value in
Bytea representation */
> > +}    option_spec_basic;
> > +
> > +
> > +/* reloptions records for specific variable types */
> > +typedef struct option_spec_bool
> > +{
> > +    option_spec_basic base;
> > +    bool        default_val;
> > +}    option_spec_bool;
> > +
> > +typedef struct option_spec_int
> > +{
> > +    option_spec_basic base;
> > +    int            default_val;
> > +    int            min;
> > +    int            max;
> > +}    option_spec_int;
> > +
> > +typedef struct option_spec_real
> > +{
> > +    option_spec_basic base;
> > +    double        default_val;
> > +    double        min;
> > +    double        max;
> > +}    option_spec_real;
> > +
> > +typedef struct option_spec_enum
> > +{
> > +    option_spec_basic base;
> > +    opt_enum_elt_def *members;/* FIXME rewrite. Null terminated array of
> > allowed values for +
 * the option */
> > +    int            default_val;    /* Number of item of
allowed_values array */
> > +    const char  *detailmsg;
> > +}    option_spec_enum;
> > +
> > +/* validation routines for strings */
> > +typedef void (*validate_string_option) (const char *value);
> > +
> > +/*
> > + * When storing sting reloptions, we shoud deal with special case when
> > + * option value is not set. For fixed length options, we just copy
> > default
> > + * option value into the binary structure. For varlen value, there can be
> > + * "not set" special case, with no default value offered.
> > + * In this case we will set offset value to -1, so code that use
> > relptions
> > + * can deal this case. For better readability it was defined as a
> > constant. + */
> > +#define OPTION_STRING_VALUE_NOT_SET_OFFSET -1
> > +
> > +typedef struct option_spec_string
> > +{
> > +    option_spec_basic base;
> > +    validate_string_option validate_cb;
> > +    char       *default_val;
> > +}    option_spec_string;
> > +
> > +typedef void (*postprocess_bytea_options_function) (void *data, bool
> > validate); +
> > +typedef struct options_spec_set
> > +{
> > +    option_spec_basic **definitions;
> > +    int            num;            /* Number of
spec_set items in use */
> > +    int            num_allocated;    /* Number of spec_set
items allocated */
> > +    bool        forbid_realloc; /* If number of items of the
spec_set were
> > +                                 * strictly
set to certain value do no allow
> > +                                 * adding
more idems */
> > +    Size        struct_size;    /* Size of a structure for
options in binary
> > +                                 *
representation */
> > +    postprocess_bytea_options_function postprocess_fun; /* This function
is
> > +
                 * called after options
> > +
                 * were converted in
> > +
                 * Bytea represenation.
> > +
                 * Can be used for extra
> > +
                 * validation and so on */
> > +    char       *namespace;        /* spec_set is used for options
from this
> > +                                 * namespase
*/
> > +}    options_spec_set;
> > +
> > +
> > +/* holds an option value parsed or unparsed */
> > +typedef struct option_value
> > +{
> > +    option_spec_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;
> > +
> > +
> > +
> > +
> > +/*
> > + * Options spec_set related functions
> > + */
> > +extern options_spec_set *allocateOptionsSpecSet(const char *namespace,
> > +                                 int
size_of_bytea, int num_items_expected);
> > +extern void optionsSpecSetAddBool(options_spec_set * spec_set, const char
> > *name, +                  const char *desc, LOCKMODE
lockmode, option_spec_flags
> > flags, +
int struct_offset, bool default_val);
> > +extern void optionsSpecSetAddInt(options_spec_set * spec_set, const char
> > *name, +                    const char *desc, LOCKMODE
lockmode, option_spec_flags
> > flags, +                    int struct_offset, int
default_val, int min_val, int
> > max_val); +extern void optionsSpecSetAddReal(options_spec_set * spec_set,
> > const char *name, +          const char *desc, LOCKMODE lockmode,
> > option_spec_flags flags, +      int struct_offset, double default_val,
> > double min_val, double max_val); +extern void
> > optionsSpecSetAddEnum(options_spec_set * spec_set,
> > +                          const char *name, const
char *desc, LOCKMODE lockmode,
> > option_spec_flags flags, +            int struct_offset,
opt_enum_elt_def*
> > members, int default_val, const char *detailmsg); +extern void
> > optionsSpecSetAddString(options_spec_set * spec_set, const char *name,
> > +          const char *desc, LOCKMODE lockmode, option_spec_flags flags,
+int
> > struct_offset, const char *default_val, validate_string_option
> > validator); +
> > +
> > +/*
> > + * This macro allows to get string option value from bytea
> > representation.
> > + * "optstruct" - is a structure that is stored in bytea options
> > representation + * "member" - member of this structure that has string
> > option value + * (actually string values are stored in bytea after the
> > structure, and + * and "member" will contain an offset to this value.
> > This macro do all + * the math
> > + */
> > +#define GET_STRING_OPTION(optstruct, member) \
> > +    ((optstruct)->member == OPTION_STRING_VALUE_NOT_SET_OFFSET ? NULL : \
> > +     (char *)(optstruct) + (optstruct)->member)
> > +
> > +/*
> > + * Functions related to option convertation, parsing, manipulation
> > + * and validation
> > + */
> > +extern void optionsDefListValdateNamespaces(List *defList,
> > +                                char
**allowed_namespaces);
> > +extern List *optionsDefListFilterNamespaces(List *defList, const char
> > *namespace); +extern List *optionsTextArrayToDefList(Datum options);
> > +extern Datum optionsDefListToTextArray(List *defList);
> > +/*
> > + * Meta functions that uses functions above to get options for relations,
> > + * tablespaces, views and so on
> > + */
> > +
> > +extern bytea *optionsTextArrayToBytea(options_spec_set * spec_set, Datum
> > data, +
                            bool validate);
> > +extern Datum transformOptions(options_spec_set * spec_set, 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 7c5fbeb..21b91df 100644
> > --- a/src/include/access/reloptions.h
> > +++ b/src/include/access/reloptions.h
> > @@ -22,6 +22,7 @@
> >
> >  #include "access/amapi.h"
> >  #include "access/htup.h"
> >  #include "access/tupdesc.h"
> >
> > +#include "access/options.h"
> >
> >  #include "nodes/pg_list.h"
> >  #include "storage/lock.h"
> >
> > @@ -110,20 +111,10 @@ typedef struct relopt_real
> >
> >      double        max;
> >
> >  } relopt_real;
> >
> > -/*
> > - * relopt_enum_elt_def -- One member of the array of acceptable values
> > - * of an enum reloption.
> > - */
> > -typedef struct relopt_enum_elt_def
> > -{
> > -    const char *string_val;
> > -    int            symbol_val;
> > -} relopt_enum_elt_def;
> > -
> >
> >  typedef struct relopt_enum
> >  {
> >
> >      relopt_gen    gen;
> >
> > -    relopt_enum_elt_def *members;
> > +    opt_enum_elt_def *members;
> >
> >      int            default_val;
> >      const char *detailmsg;
> >      /* null-terminated array of members */
> >
> > @@ -167,6 +158,7 @@ typedef struct local_relopts
> >
> >      List       *options;        /* list of local_relopt
definitions */
> >      List       *validators;        /* list of relopts_validator
callbacks */
> >      Size        relopt_struct_size; /* size of parsed bytea
structure */
> >
> > +    options_spec_set * spec_set; /* FIXME */
> >
> >  } local_relopts;
> >
> >  /*
> >
> > @@ -179,21 +171,6 @@ typedef struct local_relopts
> >
> >      ((optstruct)->member == 0 ? NULL : \
> >
> >       (char *)(optstruct) + (optstruct)->member)
> >
> > -extern relopt_kind add_reloption_kind(void);
> > -extern void add_bool_reloption(bits32 kinds, const char *name, const char
> > *desc, -                               bool
default_val, LOCKMODE lockmode);
> > -extern void add_int_reloption(bits32 kinds, const char *name, const char
> > *desc, -                              int
default_val, int min_val, int max_val,
> > -                              LOCKMODE
lockmode);
> > -extern void add_real_reloption(bits32 kinds, const char *name, const char
> > *desc, -                               double
default_val, double min_val, double max_val,
> > -                               LOCKMODE
lockmode);
> > -extern void add_enum_reloption(bits32 kinds, const char *name, const char
> > *desc, -
relopt_enum_elt_def *members, int default_val,
> > -                               const char
*detailmsg, LOCKMODE lockmode);
> > -extern void add_string_reloption(bits32 kinds, const char *name, const
> > char *desc, -
const char *default_val, validate_string_relopt
> > validator, -
LOCKMODE lockmode);
> >
> >  extern void init_local_reloptions(local_relopts *opts, Size
> >  relopt_struct_size); extern void
> >  register_reloptions_validator(local_relopts *opts,
> >
> > @@ -210,7 +187,7 @@ extern void add_local_real_reloption(local_relopts
> > *opts, const char *name,>
> >                                       int
offset);
> >
> >  extern void add_local_enum_reloption(local_relopts *relopts,
> >
> >
const char *name, const char *desc,
> >
> > -
relopt_enum_elt_def *members,
> > +
opt_enum_elt_def *members,
> >
> >                                       int
default_val, const char *detailmsg,
> >                                       int
offset);
> >
> >  extern void add_local_string_reloption(local_relopts *opts, const char
> >  *name,>
> > @@ -219,29 +196,17 @@ extern void add_local_string_reloption(local_relopts
> > *opts, const char *name,>
> >
validate_string_relopt validator,
> >
fill_string_relopt filler, int offset);
> >
> > -extern Datum transformRelOptions(Datum oldOptions, List *defList,
> > -                                 const char
*namspace, char *validnsps[],
> > -                                 bool
acceptOidsOff, bool isReset);
> > -extern List *untransformRelOptions(Datum options);
> >
> >  extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
> >
> > -
amoptions_function amoptions);
> > -extern void *build_reloptions(Datum reloptions, bool validate,
> > -                              relopt_kind kind,
> > -                              Size
relopt_struct_size,
> > -                              const
relopt_parse_elt *relopt_elems,
> > -                              int
num_relopt_elems);
> > +
amreloptspecset_function amoptions_def_set);
> >
> >  extern void *build_local_reloptions(local_relopts *relopts, Datum
> >  options,
> >
> >                                      bool
validate);
> >
> > -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 *partitioned_table_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);
> > +options_spec_set *get_heap_relopt_spec_set(void);
> > +options_spec_set *get_toast_relopt_spec_set(void);
> > +options_spec_set *get_partitioned_relopt_spec_set(void);
> > +options_spec_set *get_view_relopt_spec_set(void);
> > +options_spec_set *get_attribute_options_spec_set(void);
> > +options_spec_set *get_tablespace_options_spec_set(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 2eb2f42..d9a9b2d 100644
> > --- a/src/include/access/spgist.h
> > +++ b/src/include/access/spgist.h
> > @@ -189,9 +189,6 @@ typedef struct spgLeafConsistentOut
> >
> >  } spgLeafConsistentOut;
> >
> > -/* spgutils.c */
> > -extern bytea *spgoptions(Datum reloptions, bool validate);
> > -
> >
> >  /* spginsert.c */
> >  extern IndexBuildResult *spgbuild(Relation heap, Relation index,
> >
> >                                    struct
IndexInfo *indexInfo);
> >
> > diff --git a/src/include/access/spgist_private.h
> > b/src/include/access/spgist_private.h index 40d3b71..dd9a05a 100644
> > --- a/src/include/access/spgist_private.h
> > +++ b/src/include/access/spgist_private.h
> > @@ -529,6 +529,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistState
> > *state, Page page,>
> >  extern bool spgproperty(Oid index_oid, int attno,
> >
> >                          IndexAMProperty prop, const
char *propname,
> >                          bool *res, bool *isnull);
> >
> > +extern void *spggetreloptspecset(void);
> >
> >  /* spgdoinsert.c */
> >  extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
> >
> > diff --git a/src/include/commands/tablecmds.h
> > b/src/include/commands/tablecmds.h index 336549c..3f87f98 100644
> > --- a/src/include/commands/tablecmds.h
> > +++ b/src/include/commands/tablecmds.h
> > @@ -34,7 +34,7 @@ extern Oid    AlterTableLookupRelation(AlterTableStmt
> > *stmt, LOCKMODE lockmode);>
> >  extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
> >
> >                         struct AlterTableUtilityContext
*context);
> >
> > -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/test/modules/dummy_index_am/dummy_index_am.c
> > b/src/test/modules/dummy_index_am/dummy_index_am.c index
> > 5365b063..80b39e8 100644
> > --- a/src/test/modules/dummy_index_am/dummy_index_am.c
> > +++ b/src/test/modules/dummy_index_am/dummy_index_am.c
> > @@ -14,7 +14,7 @@
> >
> >  #include "postgres.h"
> >
> >  #include "access/amapi.h"
> >
> > -#include "access/reloptions.h"
> > +#include "access/options.h"
> >
> >  #include "catalog/index.h"
> >  #include "commands/vacuum.h"
> >  #include "nodes/pathnodes.h"
> >
> > @@ -25,12 +25,6 @@ PG_MODULE_MAGIC;
> >
> >  void        _PG_init(void);
> >
> > -/* parse table for fillRelOptions */
> > -relopt_parse_elt di_relopt_tab[6];
> > -
> > -/* Kind of relation options for dummy index */
> > -relopt_kind di_relopt_kind;
> > -
> >
> >  typedef enum DummyAmEnum
> >  {
> >
> >      DUMMY_AM_ENUM_ONE,
> >
> > @@ -49,7 +43,7 @@ typedef struct DummyIndexOptions
> >
> >      int            option_string_null_offset;
> >
> >  }            DummyIndexOptions;
> >
> > -relopt_enum_elt_def dummyAmEnumValues[] =
> > +opt_enum_elt_def dummyAmEnumValues[] =
> >
> >  {
> >
> >      {"one", DUMMY_AM_ENUM_ONE},
> >      {"two", DUMMY_AM_ENUM_TWO},
> >
> > @@ -63,77 +57,85 @@ PG_FUNCTION_INFO_V1(dihandler);
> >
> >   * Validation function for string relation options.
> >   */
> >
> >  static void
> >
> > -validate_string_option(const char *value)
> > +divalidate_string_option(const char *value)
> >
> >  {
> >
> >      ereport(NOTICE,
> >
> >              (errmsg("new option value for string parameter %s",
> >
> >                      value ? value : "NULL")));
> >
> >  }
> >
> > -/*
> > - * This function creates a full set of relation option types,
> > - * with various patterns.
> > - */
> > -static void
> > -create_reloptions_table(void)
> > +static options_spec_set *di_relopt_specset = NULL;
> > +void * digetreloptspecset(void);
> > +
> > +void *
> > +digetreloptspecset(void)
> >
> >  {
> >
> > -    di_relopt_kind = add_reloption_kind();
> > -
> > -    add_int_reloption(di_relopt_kind, "option_int",
> > -                      "Integer option for
dummy_index_am",
> > -                      10, -10, 100,
AccessExclusiveLock);
> > -    di_relopt_tab[0].optname = "option_int";
> > -    di_relopt_tab[0].opttype = RELOPT_TYPE_INT;
> > -    di_relopt_tab[0].offset = offsetof(DummyIndexOptions, option_int);
> > -
> > -    add_real_reloption(di_relopt_kind, "option_real",
> > -                       "Real option for dummy_index_am",
> > -                       3.1415, -10, 100,
AccessExclusiveLock);
> > -    di_relopt_tab[1].optname = "option_real";
> > -    di_relopt_tab[1].opttype = RELOPT_TYPE_REAL;
> > -    di_relopt_tab[1].offset = offsetof(DummyIndexOptions, option_real);
> > -
> > -    add_bool_reloption(di_relopt_kind, "option_bool",
> > -                       "Boolean option for
dummy_index_am",
> > -                       true, AccessExclusiveLock);
> > -    di_relopt_tab[2].optname = "option_bool";
> > -    di_relopt_tab[2].opttype = RELOPT_TYPE_BOOL;
> > -    di_relopt_tab[2].offset = offsetof(DummyIndexOptions, option_bool);
> > -
> > -    add_enum_reloption(di_relopt_kind, "option_enum",
> > -                       "Enum option for dummy_index_am",
> > -                       dummyAmEnumValues,
> > -                       DUMMY_AM_ENUM_ONE,
> > -                       "Valid values are \"one\" and
\"two\".",
> > -                       AccessExclusiveLock);
> > -    di_relopt_tab[3].optname = "option_enum";
> > -    di_relopt_tab[3].opttype = RELOPT_TYPE_ENUM;
> > -    di_relopt_tab[3].offset = offsetof(DummyIndexOptions, option_enum);
> > -
> > -    add_string_reloption(di_relopt_kind, "option_string_val",
> > -                         "String option for
dummy_index_am with non-NULL default",
> > -                         "DefaultValue",
&validate_string_option,
> > -                         AccessExclusiveLock);
> > -    di_relopt_tab[4].optname = "option_string_val";
> > -    di_relopt_tab[4].opttype = RELOPT_TYPE_STRING;
> > -    di_relopt_tab[4].offset = offsetof(DummyIndexOptions,
> > -
option_string_val_offset);
> > +    if (di_relopt_specset)
> > +        return di_relopt_specset;
> > +
> > +    di_relopt_specset = allocateOptionsSpecSet(NULL,
> > +
       sizeof(DummyIndexOptions), 6);
> > +
> > +    optionsSpecSetAddInt(
> > +        di_relopt_specset, "option_int",
> > +        "Integer option for dummy_index_am",
> > +        AccessExclusiveLock,
> > +        0, offsetof(DummyIndexOptions, option_int),
> > +        10, -10, 100
> > +    );
> > +
> > +
> > +    optionsSpecSetAddReal(
> > +        di_relopt_specset, "option_real",
> > +        "Real option for dummy_index_am",
> > +        AccessExclusiveLock,
> > +        0, offsetof(DummyIndexOptions, option_real),
> > +        3.1415, -10, 100
> > +    );
> > +
> > +    optionsSpecSetAddBool(
> > +        di_relopt_specset, "option_bool",
> > +        "Boolean option for dummy_index_am",
> > +        AccessExclusiveLock,
> > +        0, offsetof(DummyIndexOptions, option_bool), true
> > +    );
> > +
> > +    optionsSpecSetAddEnum(di_relopt_specset, "option_enum",
> > +        "Enum option for dummy_index_am",
> > +        AccessExclusiveLock,
> > +        0,
> > +        offsetof(DummyIndexOptions, option_enum),
> > +        dummyAmEnumValues,
> > +        DUMMY_AM_ENUM_ONE,
> > +        "Valid values are \"one\" and \"two\"."
> > +    );
> > +
> > +    optionsSpecSetAddString(di_relopt_specset, "option_string_val",
> > +        "String option for dummy_index_am with non-NULL default",
> > +        AccessExclusiveLock,
> > +        0,
> > +        offsetof(DummyIndexOptions, option_string_val_offset),
> > +        "DefaultValue", &divalidate_string_option
> > +    );
> >
> >      /*
> >
> >       * String option for dummy_index_am with NULL default, and without
> >       * description.
> >       */
> >
> > -    add_string_reloption(di_relopt_kind, "option_string_null",
> > -                         NULL,    /* description */
> > -                         NULL,
&validate_string_option,
> > -                         AccessExclusiveLock);
> > -    di_relopt_tab[5].optname = "option_string_null";
> > -    di_relopt_tab[5].opttype = RELOPT_TYPE_STRING;
> > -    di_relopt_tab[5].offset = offsetof(DummyIndexOptions,
> > -
option_string_null_offset);
> > +
> > +    optionsSpecSetAddString(di_relopt_specset, "option_string_null",
> > +        NULL,    /* description */
> > +        AccessExclusiveLock,
> > +        0,
> > +        offsetof(DummyIndexOptions, option_string_null_offset),
> > +        NULL, &divalidate_string_option
> > +    );
> > +
> > +    return di_relopt_specset;
> >
> >  }
> >
> > +
> >
> >  /*
> >
> >   * Build a new index.
> >   */
> >
> > @@ -219,19 +221,6 @@ dicostestimate(PlannerInfo *root, IndexPath *path,
> > double loop_count,>
> >  }
> >
> >  /*
> >
> > - * Parse relation options for index AM, returning a DummyIndexOptions
> > - * structure filled with option values.
> > - */
> > -static bytea *
> > -dioptions(Datum reloptions, bool validate)
> > -{
> > -    return (bytea *) build_reloptions(reloptions, validate,
> > -
di_relopt_kind,
> > -
sizeof(DummyIndexOptions),
> > -
di_relopt_tab, lengthof(di_relopt_tab));
> > -}
> > -
> > -/*
> >
> >   * Validator for index AM.
> >   */
> >
> >  static bool
> >
> > @@ -308,7 +297,6 @@ dihandler(PG_FUNCTION_ARGS)
> >
> >      amroutine->amvacuumcleanup = divacuumcleanup;
> >      amroutine->amcanreturn = NULL;
> >      amroutine->amcostestimate = dicostestimate;
> >
> > -    amroutine->amoptions = dioptions;
> >
> >      amroutine->amproperty = NULL;
> >      amroutine->ambuildphasename = NULL;
> >      amroutine->amvalidate = divalidate;
> >
> > @@ -322,12 +310,7 @@ dihandler(PG_FUNCTION_ARGS)
> >
> >      amroutine->amestimateparallelscan = NULL;
> >      amroutine->aminitparallelscan = NULL;
> >      amroutine->amparallelrescan = NULL;
> >
> > +    amroutine->amreloptspecset = digetreloptspecset;
> >
> >      PG_RETURN_POINTER(amroutine);
> >
> >  }
> >
> > -
> > -void
> > -_PG_init(void)
> > -{
> > -    create_reloptions_table();
> > -}







Re: Suggestion: Unified options API. Need help from core team

От
Bruce Momjian
Дата:
On Fri, Nov 26, 2021 at 11:19:16AM +0300, Nikolay Shaplov wrote:
> В письме от вторник, 26 октября 2021 г. 17:25:32 MSK пользователь Bruce 
> Momjian написал:
> > Uh, the core team does not get involved in development issues, unless
> > there is a issue that clearly cannot be resolved by discussion on the
> > hackers list.
> Then may be I used wrong therm. May be I should say "experienced postgres 
> developers". 

OK, but "experienced Postgres developers" are by definition on the
hackers email list, not necessarily on the core team.  In fact, some
core team members are not Postgres backend developers.

---------------------------------------------------------------------------

> > 
> > On Mon, Oct 18, 2021 at 04:24:23PM +0300, Nikolay Shaplov wrote:
> > > Hi!
> > > 
> > > I am still hoping to finish my work on reloptions I've started some years
> > > ago.
> > > 
> > > I've renewed my patch and I think I need help from core team to finish it.
> > > 
> > > General idea of the patch: Now we have three ways to define options for
> > > different objects, with more or less different code used for it. It wold
> > > be
> > > better to have unified context independent API for processing options,
> > > instead.
> > > 
> > > Long story short:
> > > 
> > > There is Option Specification object, that has all information about
> > > single
> > > option, how it should be parsed and validated.
> > > 
> > > There is Option Specification Set object, an array of Option Specs, that
> > > defines all options available for certain object (am of some index for
> > > example).
> > > 
> > > When some object (relation, opclass,  etc) wants to have an options, it
> > > creates an Option Spec Set for there options, and uses it for converting
> > > options between different representations (to get is from SQL, to store it
> > > in pg_class, to pass it to the core code as bytea etc)
> > > 
> > > For indexes Option Spec Set is available via Access Method API.
> > > 
> > > For non-index relations all Option Spec Sets are left in reloption.c file,
> > > and should be moved to heap AM later. (They are not in AM now so will not
> > > change it now)
> > > 
> > > Main problem:
> > > 
> > > There are LockModes. LockModes for options is also stored in Option Spec
> > > Set. For indexes Option Spec Sec is accessable via AM. So to get LockMode
> > > for option of an index you need to have access for it's relation object
> > > (so you can call proper AM method to fetch spec set). So you need
> > > "Relation rel" in AlterTableGetRelOptionsLockLevel where Lock Level is
> > > determinated (src/ backend/access/common/reloptions.c)
> > > AlterTableGetRelOptionsLockLevel is called from AlterTableGetLockLevel
> > > (src/ backend/commands/tablecmds.c) so we need "Relation rel" there too.
> > > AlterTableGetLockLevel is called from AlterTableInternal (/src/backend/
> > > commands/tablecmds.c) There we have "Oid relid" so we can try to open
> > > relation like this
> > > 
> > >                    Relation rel = relation_open(relid, NoLock);
> > >                    cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
> > >                    
> > >                                                    castNode(List,
> > >                                                    cmd->def));
> > >                    
> > >                    relation_close(rel,NoLock);
> > >                    break;
> > > 
> > > but this will trigger the assertion
> > > 
> > >    Assert(lockmode != NoLock ||
> > >    
> > >           IsBootstrapProcessingMode() ||
> > >           CheckRelationLockedByMe(r, c, true));
> > > 
> > > in relation_open (b/src/backend/access/common/relation.c)
> > > 
> > > For now I've commented this assertion out. I've tried to open relation
> > > with
> > > AccessShareLock but this caused one test to fail, and I am not sure this
> > > solution is better.
> > > 
> > > What I have done here I consider a hack, so I need a help of core-team
> > > here to do it in right way.
> > > 
> > > General problems:
> > > 
> > > I guess I need a coauthor, or supervisor from core team, to finish this
> > > patch. The amount of code is big, and I guess there are parts that can be
> > > made more in postgres way, then I did them. And I would need an advice
> > > there, and I guess it would be better to do if before sending it to
> > > commitfest.
> > > 
> > > 
> > > Current patch status:
> > > 
> > > 1. It is Beta. Some minor issues and FIXMEs are not solved. Some code
> > > comments needs revising, but in general it do what it is intended to do.
> > > 
> > > 2. This patch does not intend to change postgres behavior at all, all
> > > should work as before, all changes are internal only.
> > > 
> > > The only exception is error message for unexciting option name in toast
> > > namespace
> > > 
> > >  CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option =
> > >  42);> 
> > > -ERROR:  unrecognized parameter "not_existing_option"
> > > +ERROR:  unrecognized parameter "toast.not_existing_option"
> > > 
> > > New message is better I guess, though I can change it back if needed.
> > > 
> > > 3. I am doing my development in this blanch
> > > https://gitlab.com/dhyannataraj/ postgres/-/tree/new_options_take_two I
> > > am making changes every day, so last version will be available there
> > > 
> > > Would be glad to hear from coreteam before I finish with this patch and
> > > made it ready for commit-fest.
> > > 
> > > 
> > > 
> > > diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
> > > index a22a6df..8f2d5e7 100644
> > > --- a/contrib/bloom/bloom.h
> > > +++ b/contrib/bloom/bloom.h
> > > @@ -17,6 +17,7 @@
> > > 
> > >  #include "access/generic_xlog.h"
> > >  #include "access/itup.h"
> > >  #include "access/xlog.h"
> > > 
> > > +#include "access/options.h"
> > > 
> > >  #include "fmgr.h"
> > >  #include "nodes/pathnodes.h"
> > > 
> > > @@ -207,7 +208,8 @@ extern IndexBulkDeleteResult
> > > *blbulkdelete(IndexVacuumInfo *info,> 
> > >                                          
>    void *callback_state);
> > >  
> > >  extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
> > >  
> > >                                          
>       IndexBulkDeleteResult *stats);
> > > 
> > > -extern bytea *bloptions(Datum reloptions, bool validate);
> > > +extern void *blrelopt_specset(void);
> > > +extern void blReloptionPostprocess(void *, bool validate);
> > > 
> > >  extern void blcostestimate(PlannerInfo *root, IndexPath *path,
> > >  
> > >                             double loop_count, Cost 
> *indexStartupCost,
> > >                             Cost *indexTotalCost, 
> Selectivity *indexSelectivity,
> > > 
> > > diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
> > > index 754de00..54dad16 100644
> > > --- a/contrib/bloom/blutils.c
> > > +++ b/contrib/bloom/blutils.c
> > > @@ -15,7 +15,7 @@
> > > 
> > >  #include "access/amapi.h"
> > >  #include "access/generic_xlog.h"
> > > 
> > > -#include "access/reloptions.h"
> > > +#include "access/options.h"
> > > 
> > >  #include "bloom.h"
> > >  #include "catalog/index.h"
> > >  #include "commands/vacuum.h"
> > > 
> > > @@ -34,53 +34,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_spec_set *bl_relopt_specset;
> > > 
> > >  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,
> > > -                      AccessExclusiveLock);
> > > -    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,
> > > -                          AccessExclusiveLock);
> > > -        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; -    }
> > > -}
> > > -
> > > -/*
> > > 
> > >   * Construct a default set of Bloom options.
> > >   */
> > >  
> > >  static BloomOptions *
> > > 
> > > @@ -135,7 +95,7 @@ blhandler(PG_FUNCTION_ARGS)
> > > 
> > >      amroutine->amvacuumcleanup = blvacuumcleanup;
> > >      amroutine->amcanreturn = NULL;
> > >      amroutine->amcostestimate = blcostestimate;
> > > 
> > > -    amroutine->amoptions = bloptions;
> > > +    amroutine->amreloptspecset = blrelopt_specset;
> > > 
> > >      amroutine->amproperty = NULL;
> > >      amroutine->ambuildphasename = NULL;
> > >      amroutine->amvalidate = blvalidate;
> > > 
> > > @@ -154,6 +114,28 @@ blhandler(PG_FUNCTION_ARGS)
> > > 
> > >      PG_RETURN_POINTER(amroutine);
> > >  
> > >  }
> > > 
> > > +void
> > > +blReloptionPostprocess(void *data, bool validate)
> > > +{
> > > +    BloomOptions *opts = (BloomOptions *) data;
> > > +    int            i;
> > > +
> > > +    if (validate)
> > > +        for (i = 0; i < INDEX_MAX_KEYS; i++)
> > > +        {
> > > +            if (opts->bitSize[i] >= opts->bloomLength)
> > > +            {
> > > +                ereport(ERROR,
> > > +                        
> (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > > +                       errmsg("col%i should not be grater 
> than length", i)));
> > > +            }
> > > +        }
> > > +
> > > +    /* Convert signature length from # of bits to # to words, rounding up 
> */
> > > +    opts->bloomLength = (opts->bloomLength + SIGNWORDBITS - 1) /
> > > SIGNWORDBITS; +}
> > > +
> > > +
> > > 
> > >  /*
> > >  
> > >   * Fill BloomState structure for particular index.
> > >   */
> > > 
> > > @@ -474,24 +456,39 @@ BloomInitMetapage(Relation index)
> > > 
> > >      UnlockReleaseBuffer(metaBuffer);
> > >  
> > >  }
> > > 
> > > -/*
> > > - * Parse reloptions for bloom index, producing a BloomOptions struct.
> > > - */
> > > -bytea *
> > > -bloptions(Datum reloptions, bool validate)
> > > +void *
> > > +blrelopt_specset(void)
> > > 
> > >  {
> > > 
> > > -    BloomOptions *rdopts;
> > > +    int            i;
> > > +    char        buf[16];
> > > 
> > > -    /* Parse the user-given reloptions */
> > > -    rdopts = (BloomOptions *) build_reloptions(reloptions, validate,
> > > -                                        
>        bl_relopt_kind,
> > > -                                        
>        sizeof(BloomOptions),
> > > -                                        
>        bl_relopt_tab,
> > > -                                        
>        lengthof(bl_relopt_tab));
> > > +    if (bl_relopt_specset)
> > > +        return bl_relopt_specset;
> > > 
> > > -    /* Convert signature length from # of bits to # to words, rounding 
> up */
> > > -    if (rdopts)
> > > -        rdopts->bloomLength = (rdopts->bloomLength + SIGNWORDBITS - 
> 1) /
> > > SIGNWORDBITS;
> > > 
> > > -    return (bytea *) rdopts;
> > > +    bl_relopt_specset = allocateOptionsSpecSet(NULL,
> > > +                               
> sizeof(BloomOptions), INDEX_MAX_KEYS + 1);
> > > +    bl_relopt_specset->postprocess_fun = blReloptionPostprocess;
> > > +
> > > +    optionsSpecSetAddInt(bl_relopt_specset, "length",
> > > +                             "Length of signature 
> in bits",
> > > +                             NoLock,        /* 
> No lock as far as ALTER is
> > > +                                        
>      * forbidden */
> > > +                             0,
> > > +                             
> offsetof(BloomOptions, bloomLength),
> > > +                             
> DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH);
> > > +
> > > +    /* 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);
> > > +        optionsSpecSetAddInt(bl_relopt_specset, buf,
> > > +                               "Number of bits 
> for corresponding column",
> > > +                                 NoLock,    /* 
> No lock as far as ALTER is
> > > +                                        
>      * forbidden */
> > > +                                 0,
> > > +                                 
> offsetof(BloomOptions, bitSize[i]),
> > > +                                 
> DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS);
> > > +    }
> > > +    return bl_relopt_specset;
> > > 
> > >  }
> > > 
> > > diff --git a/contrib/bloom/expected/bloom.out
> > > b/contrib/bloom/expected/bloom.out index dae12a7..e79456d 100644
> > > --- a/contrib/bloom/expected/bloom.out
> > > +++ b/contrib/bloom/expected/bloom.out
> > > @@ -228,3 +228,6 @@ CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH
> > > (length=0);> 
> > >  ERROR:  value 0 out of bounds for option "length"
> > >  CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
> > >  ERROR:  value 0 out of bounds for option "col1"
> > > 
> > > +-- 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
> > > diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
> > > index 4733e1e..0bfc767 100644
> > > --- a/contrib/bloom/sql/bloom.sql
> > > +++ b/contrib/bloom/sql/bloom.sql
> > > @@ -93,3 +93,6 @@ SELECT reloptions FROM pg_class WHERE oid =
> > > 'bloomidx'::regclass;> 
> > >  \set VERBOSITY terse
> > >  CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
> > >  CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
> > > 
> > > +
> > > +-- check post_validate for colN<lengh
> > > +CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH
> > > (length=10,col1=11);
> > > diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
> > > index 3a0beaa..a15a10b 100644
> > > --- a/contrib/dblink/dblink.c
> > > +++ b/contrib/dblink/dblink.c
> > > @@ -2005,7 +2005,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 2c2f149..1194747 100644
> > > --- a/contrib/file_fdw/file_fdw.c
> > > +++ b/contrib/file_fdw/file_fdw.c
> > > @@ -195,7 +195,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 5bb1af4..bbd4167 100644
> > > --- a/contrib/postgres_fdw/option.c
> > > +++ b/contrib/postgres_fdw/option.c
> > > @@ -72,7 +72,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 ccc9fa0..5dd52a4 100644
> > > --- a/src/backend/access/brin/brin.c
> > > +++ b/src/backend/access/brin/brin.c
> > > @@ -20,7 +20,6 @@
> > > 
> > >  #include "access/brin_pageops.h"
> > >  #include "access/brin_xlog.h"
> > >  #include "access/relation.h"
> > > 
> > > -#include "access/reloptions.h"
> > > 
> > >  #include "access/relscan.h"
> > >  #include "access/table.h"
> > >  #include "access/tableam.h"
> > > 
> > > @@ -40,7 +39,6 @@
> > > 
> > >  #include "utils/memutils.h"
> > >  #include "utils/rel.h"
> > > 
> > > -
> > > 
> > >  /*
> > >  
> > >   * We use a BrinBuildState during initial construction of a BRIN index.
> > >   * The running state is kept in a BrinMemTuple.
> > > 
> > > @@ -119,7 +117,6 @@ brinhandler(PG_FUNCTION_ARGS)
> > > 
> > >      amroutine->amvacuumcleanup = brinvacuumcleanup;
> > >      amroutine->amcanreturn = NULL;
> > >      amroutine->amcostestimate = brincostestimate;
> > > 
> > > -    amroutine->amoptions = brinoptions;
> > > 
> > >      amroutine->amproperty = NULL;
> > >      amroutine->ambuildphasename = NULL;
> > >      amroutine->amvalidate = brinvalidate;
> > > 
> > > @@ -134,6 +131,7 @@ brinhandler(PG_FUNCTION_ARGS)
> > > 
> > >      amroutine->amestimateparallelscan = NULL;
> > >      amroutine->aminitparallelscan = NULL;
> > >      amroutine->amparallelrescan = NULL;
> > > 
> > > +    amroutine->amreloptspecset = bringetreloptspecset;
> > > 
> > >      PG_RETURN_POINTER(amroutine);
> > >  
> > >  }
> > > 
> > > @@ -963,23 +961,6 @@ brinvacuumcleanup(IndexVacuumInfo *info,
> > > IndexBulkDeleteResult *stats)> 
> > >  }
> > >  
> > >  /*
> > > 
> > > - * reloptions processor for BRIN indexes
> > > - */
> > > -bytea *
> > > -brinoptions(Datum reloptions, bool validate)
> > > -{
> > > -    static const relopt_parse_elt tab[] = {
> > > -        {"pages_per_range", RELOPT_TYPE_INT, offsetof(BrinOptions,
> > > pagesPerRange)}, -        {"autosummarize", RELOPT_TYPE_BOOL,
> > > offsetof(BrinOptions, autosummarize)} -    };
> > > -
> > > -    return (bytea *) build_reloptions(reloptions, validate,
> > > -                                      
> RELOPT_KIND_BRIN,
> > > -                                      
> sizeof(BrinOptions),
> > > -                                      
> tab, lengthof(tab));
> > > -}
> > > -
> > > -/*
> > > 
> > >   * SQL-callable function to scan through an index and summarize all
> > >   ranges
> > >   * that are not currently summarized.
> > >   */
> > > 
> > > @@ -1765,3 +1746,32 @@ check_null_keys(BrinValues *bval, ScanKey
> > > *nullkeys, int nnullkeys)> 
> > >      return true;
> > >  
> > >  }
> > > 
> > > +
> > > +static options_spec_set *brin_relopt_specset = NULL;
> > > +
> > > +void *
> > > +bringetreloptspecset(void)
> > > +{
> > > +    if (brin_relopt_specset)
> > > +        return brin_relopt_specset;
> > > +    brin_relopt_specset = allocateOptionsSpecSet(NULL,
> > > +                                        
>          sizeof(BrinOptions), 2);
> > > +
> > > +    optionsSpecSetAddInt(brin_relopt_specset, "pages_per_range",
> > > +           "Number of pages that each page range covers in a BRIN 
> index",
> > > +                             NoLock,        /* 
> since ALTER is not allowed
> > > +                                        
>      * no lock needed */
> > > +                             0,
> > > +                             offsetof(BrinOptions, 
> pagesPerRange),
> > > +                             
> BRIN_DEFAULT_PAGES_PER_RANGE,
> > > +                             
> BRIN_MIN_PAGES_PER_RANGE,
> > > +                             
> BRIN_MAX_PAGES_PER_RANGE);
> > > +        optionsSpecSetAddBool(brin_relopt_specset, "autosummarize",
> > > +                    "Enables automatic summarization on 
> this BRIN index",
> > > +                              
> AccessExclusiveLock,
> > > +                              0,
> > > +                              
> offsetof(BrinOptions, autosummarize),
> > > +                              false);
> > > +    return brin_relopt_specset;
> > > +}
> > > +
> > > diff --git a/src/backend/access/brin/brin_pageops.c
> > > b/src/backend/access/brin/brin_pageops.c index df9ffc2..1940b3d 100644
> > > --- a/src/backend/access/brin/brin_pageops.c
> > > +++ b/src/backend/access/brin/brin_pageops.c
> > > @@ -420,6 +420,9 @@ brin_doinsert(Relation idxrel, BlockNumber
> > > pagesPerRange,> 
> > >          freespace = br_page_get_freespace(page);
> > >      
> > >      ItemPointerSet(&tid, blk, off);
> > > 
> > > +
> > > +//elog(WARNING, "pages_per_range = %i", pagesPerRange);
> > > +
> > > 
> > >      brinSetHeapBlockItemptr(revmapbuf, pagesPerRange, heapBlk, tid);
> > >      MarkBufferDirty(revmapbuf);
> > > 
> > > diff --git a/src/backend/access/common/Makefile
> > > b/src/backend/access/common/Makefile index b9aff0c..78c9c5a 100644
> > > --- a/src/backend/access/common/Makefile
> > > +++ b/src/backend/access/common/Makefile
> > > @@ -18,6 +18,7 @@ OBJS = \
> > > 
> > >      detoast.o \
> > >      heaptuple.o \
> > >      indextuple.o \
> > > 
> > > +    options.o \
> > > 
> > >      printsimple.o \
> > >      printtup.o \
> > >      relation.o \
> > > 
> > > diff --git a/src/backend/access/common/options.c
> > > b/src/backend/access/common/options.c new file mode 100644
> > > index 0000000..752cddc
> > > --- /dev/null
> > > +++ b/src/backend/access/common/options.c
> > > @@ -0,0 +1,1468 @@
> > > +/*-----------------------------------------------------------------------
> > > -- + *
> > > + * options.c
> > > + *      An unifom, context-free API for processing name=value options. 
> Used
> > > + *      to process relation optons (reloptions), attribute options, 
> opclass
> > > + *      options, etc.
> > > + *
> > > + * Portions Copyright (c) 1996-2021, 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"
> > > +
> > > +
> > > +/*
> > > + * OPTIONS SPECIFICATION and OPTION SPECIFICATION SET
> > > + *
> > > + * Each option is defined via Option Specification object (Option Spec).
> > > + * Option Spec should have all information that is needed for processing
> > > + * (parsing, validating, converting) of a single option. Implemented via
> > > set of + * option_spec_* structures.
> > > + *
> > > + * A set of Option Specs (Options Spec Set), defines all options
> > > available for + * certain object (certain relation kind for example). It
> > > is a list of + * Options Specs, plus validation functions that can be
> > > used to validate whole + * option set, if needed. Implemenred via
> > > options_spec_set structure and set of + * optionsSpecSetAdd* functions
> > > that are used for adding Option Specs items to + * a Set.
> > > + *
> > > + * NOTE: we choose therm "sepcification" instead of "definition" because
> > > therm + * "definition" is used for objects that came from lexer. So to
> > > avoud confusion + * here we have Option Specifications, and all
> > > "definitions" are from lexer. + */
> > > +
> > > +/*
> > > + * OPTION VALUES REPRESENTATIONS
> > > + *
> > > + * Option values usually came from lexer in form of defList obect, stored
> > > in + * pg_catalog as text array, and used when they are stored in memory
> > > as + * C-structure. These are different option values representations.
> > > Here goes + * brief description of all representations used in the code.
> > > + *
> > > + * Values
> > > + *
> > > + * Values are an internal representation that is used while converting
> > > + * Values between other representation. Value is called "parsed",
> > > + * when Value's value is converted to a proper type and validated, or is
> > > called + * "unparsed", when Value's value is stored as raw string that
> > > was obtained + * from the source without any cheks. In convertation
> > > funcion names first case + * is refered as Values, second case is refered
> > > as RawValues. Values is + * implemented as List of option_value
> > > C-structures.
> > > + *
> > > + * defList
> > > + *
> > > + * Options in form of definition List that comes from lexer. (For
> > > reloptions it + * is a part of SQL query that goes after WITH, SET or
> > > RESET keywords). Can be + * converted to and from Values using
> > > optionsDefListToRawValues and + * optionsTextArrayToRawValues functions.
> > > + *
> > > + * TEXT[]
> > > + *
> > > + * Options in form suitable for storig in TEXT[] field in DB. (E.g.
> > > reloptions + * are stores in pg_catalog.pg_class table in reloptions
> > > field). Can be converted + * to and from Values using
> > > optionsValuesToTextArray and optionsTextArrayToRawValues + * functions.
> > > + *
> > > + * Bytea
> > > + *
> > > + * Option data stored in C-structure with varlena header in the beginning
> > > of the + * structure. This representation is used to pass option values
> > > to the core + * postgres. It is fast to read, it can be cached and so on.
> > > Bytea rpresentation + * can be obtained from Vales using
> > > optionsValuesToBytea function, and can't be + * converted back.
> > > + */
> > > +
> > > +static option_spec_basic *allocateOptionSpec(int type, const char *name,
> > > +                         const char *desc, LOCKMODE 
> lockmode,
> > > +                         option_spec_flags flags, int 
> struct_offset);
> > > +
> > > +static void parse_one_option(option_value * option, const char *text_str,
> > > +                 int text_len, bool validate);
> > > +static void *optionsAllocateBytea(options_spec_set * spec_set, List
> > > *options); +
> > > +
> > > +static List *
> > > +optionsDefListToRawValues(List *defList, options_parse_mode
> > > +                          parse_mode);
> > > +static Datum optionsValuesToTextArray(List *options_values);
> > > +static List *optionsMergeOptionValues(List *old_options, List
> > > *new_options); +static bytea *optionsValuesToBytea(List *options,
> > > options_spec_set * spec_set); +List *optionsTextArrayToRawValues(Datum
> > > array_datum);
> > > +List *optionsParseRawValues(List *raw_values, options_spec_set *
> > > spec_set,
> > > +                      options_parse_mode mode);
> > > +
> > > +
> > > +/*
> > > + * Options spec_set 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_spec_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.
> > > +*/
> > > +
> > > +/*
> > > + * allocateOptionsSpecSet
> > > + *        Creates new Option Spec Set object: Allocates memory and 
> initializes
> > > + *        structure members.
> > > + *
> > > + * Spec Set items can be add via allocateOptionSpec and
> > > optionSpecSetAddItem functions + * or by calling directly any of
> > > optionsSpecSetAdd* function (preferable way) + *
> > > + * namespace - Spec Set can be bind to certain namespace (E.g.
> > > + * namespace.option=value). Options from other namespaces will be ignored
> > > while + * processing. If set to NULL, no namespace will be used at all.
> > > + *
> > > + * size_of_bytea - size of target structure of Bytea options
> > > represenation
> > > + *
> > > + * num_items_expected - if you know expected number of Spec Set items set
> > > it here. + * Set to -1 in other cases. num_items_expected will be used
> > > for preallocating memory + * and will trigger error, if you try to add
> > > more items than you expected. + */
> > > +
> > > +options_spec_set *
> > > +allocateOptionsSpecSet(const char *namespace, int size_of_bytea, int
> > > num_items_expected) +{
> > > +    MemoryContext oldcxt;
> > > +    options_spec_set *spec_set;
> > > +
> > > +    oldcxt = MemoryContextSwitchTo(TopMemoryContext);
> > > +    spec_set = palloc(sizeof(options_spec_set));
> > > +    if (namespace)
> > > +    {
> > > +        spec_set->namespace = palloc(strlen(namespace) + 1);
> > > +        strcpy(spec_set->namespace, namespace);
> > > +    }
> > > +    else
> > > +        spec_set->namespace = NULL;
> > > +    if (num_items_expected > 0)
> > > +    {
> > > +        spec_set->num_allocated = num_items_expected;
> > > +        spec_set->forbid_realloc = true;
> > > +        spec_set->definitions = palloc(
> > > +                 spec_set->num_allocated * 
> sizeof(option_spec_basic *));
> > > +    }
> > > +    else
> > > +    {
> > > +        spec_set->num_allocated = 0;
> > > +        spec_set->forbid_realloc = false;
> > > +        spec_set->definitions = NULL;
> > > +    }
> > > +    spec_set->num = 0;
> > > +    spec_set->struct_size = size_of_bytea;
> > > +    spec_set->postprocess_fun = NULL;
> > > +    MemoryContextSwitchTo(oldcxt);
> > > +    return spec_set;
> > > +}
> > > +
> > > +/*
> > > + * allocateOptionSpec
> > > + *        Allocates a new Option Specifiation object of desired type 
> and
> > > + *        initialize the type-independent fields
> > > + */
> > > +static option_spec_basic *
> > > +allocateOptionSpec(int type, const char *name, const char *desc, LOCKMODE
> > > lockmode, +                         option_spec_flags 
> flags, int struct_offset)
> > > +{
> > > +    MemoryContext oldcxt;
> > > +    size_t        size;
> > > +    option_spec_basic *newoption;
> > > +
> > > +    oldcxt = MemoryContextSwitchTo(TopMemoryContext);
> > > +
> > > +    switch (type)
> > > +    {
> > > +        case OPTION_TYPE_BOOL:
> > > +            size = sizeof(option_spec_bool);
> > > +            break;
> > > +        case OPTION_TYPE_INT:
> > > +            size = sizeof(option_spec_int);
> > > +            break;
> > > +        case OPTION_TYPE_REAL:
> > > +            size = sizeof(option_spec_real);
> > > +            break;
> > > +        case OPTION_TYPE_ENUM:
> > > +            size = sizeof(option_spec_enum);
> > > +            break;
> > > +        case OPTION_TYPE_STRING:
> > > +            size = sizeof(option_spec_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;
> > > +}
> > > +
> > > +/*
> > > + * optionSpecSetAddItem
> > > + *        Adds pre-created Option Specification objec to the Spec Set
> > > + */
> > > +static void
> > > +optionSpecSetAddItem(option_spec_basic * newoption,
> > > +                     options_spec_set * spec_set)
> > > +{
> > > +    if (spec_set->num >= spec_set->num_allocated)
> > > +    {
> > > +        MemoryContext oldcxt;
> > > +
> > > +        Assert(!spec_set->forbid_realloc);
> > > +        oldcxt = MemoryContextSwitchTo(TopMemoryContext);
> > > +
> > > +        if (spec_set->num_allocated == 0)
> > > +        {
> > > +            spec_set->num_allocated = 8;
> > > +            spec_set->definitions = palloc(
> > > +                 spec_set->num_allocated * 
> sizeof(option_spec_basic *));
> > > +        }
> > > +        else
> > > +        {
> > > +            spec_set->num_allocated *= 2;
> > > +            spec_set->definitions = repalloc(spec_set->definitions,
> > > +                 spec_set->num_allocated * 
> sizeof(option_spec_basic *));
> > > +        }
> > > +        MemoryContextSwitchTo(oldcxt);
> > > +    }
> > > +    spec_set->definitions[spec_set->num] = newoption;
> > > +    spec_set->num++;
> > > +}
> > > +
> > > +
> > > +/*
> > > + * optionsSpecSetAddBool
> > > + *        Adds boolean Option Specification entry to the Spec Set
> > > + */
> > > +void
> > > +optionsSpecSetAddBool(options_spec_set * spec_set, const char *name,
> > > const char *desc, +                          
> LOCKMODE lockmode, option_spec_flags flags,
> > > +                          int struct_offset, bool 
> default_val)
> > > +{
> > > +    option_spec_bool *spec_set_item;
> > > +
> > > +    spec_set_item = (option_spec_bool *)
> > > +        allocateOptionSpec(OPTION_TYPE_BOOL, name, desc, lockmode,
> > > +                                 flags, 
> struct_offset);
> > > +
> > > +    spec_set_item->default_val = default_val;
> > > +
> > > +    optionSpecSetAddItem((option_spec_basic *) spec_set_item, spec_set);
> > > +}
> > > +
> > > +/*
> > > + * optionsSpecSetAddInt
> > > + *        Adds integer Option Specification entry to the Spec Set
> > > + */
> > > +void
> > > +optionsSpecSetAddInt(options_spec_set * spec_set, const char *name,
> > > +          const char *desc, LOCKMODE lockmode, option_spec_flags flags,
> > > +                int struct_offset, int default_val, int 
> min_val, int max_val)
> > > +{
> > > +    option_spec_int *spec_set_item;
> > > +
> > > +    spec_set_item = (option_spec_int *)
> > > +        allocateOptionSpec(OPTION_TYPE_INT, name, desc, lockmode,
> > > +                                 flags, 
> struct_offset);
> > > +
> > > +    spec_set_item->default_val = default_val;
> > > +    spec_set_item->min = min_val;
> > > +    spec_set_item->max = max_val;
> > > +
> > > +    optionSpecSetAddItem((option_spec_basic *) spec_set_item, spec_set);
> > > +}
> > > +
> > > +/*
> > > + * optionsSpecSetAddReal
> > > + *        Adds float Option Specification entry to the Spec Set
> > > + */
> > > +void
> > > +optionsSpecSetAddReal(options_spec_set * spec_set, const char *name,
> > > const char *desc, +         LOCKMODE lockmode, option_spec_flags 
> flags, int
> > > struct_offset, +                          double 
> default_val, double min_val, double
> > > max_val)
> > > +{
> > > +    option_spec_real *spec_set_item;
> > > +
> > > +    spec_set_item = (option_spec_real *)
> > > +        allocateOptionSpec(OPTION_TYPE_REAL, name, desc, lockmode,
> > > +                                 flags, 
> struct_offset);
> > > +
> > > +    spec_set_item->default_val = default_val;
> > > +    spec_set_item->min = min_val;
> > > +    spec_set_item->max = max_val;
> > > +
> > > +    optionSpecSetAddItem((option_spec_basic *) spec_set_item, spec_set);
> > > +}
> > > +
> > > +/*
> > > + * optionsSpecSetAddEnum
> > > + *        Adds enum Option Specification entry to the Spec Set
> > > + *
> > > + * The members array must have a terminating NULL entry.
> > > + *
> > > + * The detailmsg is shown when unsupported values are passed, and has
> > > this
> > > + * form:   "Valid values are \"foo\", \"bar\", and \"bar\"."
> > > + *
> > > + * The members array and detailmsg are not copied -- caller must ensure
> > > that + * they are valid throughout the life of the process.
> > > + */
> > > +
> > > +void
> > > +optionsSpecSetAddEnum(options_spec_set * spec_set, const char *name,
> > > const char *desc, +        LOCKMODE lockmode, option_spec_flags flags, 
> int
> > > struct_offset,
> > > +        opt_enum_elt_def * members, int default_val, const char 
> *detailmsg)
> > > +{
> > > +    option_spec_enum *spec_set_item;
> > > +
> > > +    spec_set_item = (option_spec_enum *)
> > > +        allocateOptionSpec(OPTION_TYPE_ENUM, name, desc, lockmode,
> > > +                                 flags, 
> struct_offset);
> > > +
> > > +    spec_set_item->default_val = default_val;
> > > +    spec_set_item->members = members;
> > > +    spec_set_item->detailmsg = detailmsg;
> > > +
> > > +    optionSpecSetAddItem((option_spec_basic *) spec_set_item, spec_set);
> > > +}
> > > +
> > > +/*
> > > + * optionsSpecSetAddString
> > > + *        Adds string Option Specification entry to the Spec Set
> > > + *
> > > + * "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
> > > +optionsSpecSetAddString(options_spec_set * spec_set, const char *name,
> > > const char *desc, +         LOCKMODE lockmode, option_spec_flags 
> flags, int
> > > struct_offset, +                   const char *default_val, 
> validate_string_option
> > > validator) +{
> > > +    option_spec_string *spec_set_item;
> > > +
> > > +    /* make sure the validator/default combination is sane */
> > > +    if (validator)
> > > +        (validator) (default_val);
> > > +
> > > +    spec_set_item = (option_spec_string *)
> > > +        allocateOptionSpec(OPTION_TYPE_STRING, name, desc, lockmode,
> > > +                                 flags, 
> struct_offset);
> > > +    spec_set_item->validate_cb = validator;
> > > +
> > > +    if (default_val)
> > > +        spec_set_item->default_val = 
> MemoryContextStrdup(TopMemoryContext,
> > > +                                        
>                 default_val);
> > > +    else
> > > +        spec_set_item->default_val = NULL;
> > > +    optionSpecSetAddItem((option_spec_basic *) spec_set_item, spec_set);
> > > +}
> > > +
> > > +
> > > +/*
> > > + * Options transform functions
> > > + */
> > > +
> > > +/* FIXME this comment should be updated
> > > + * 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 spec_set + * 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 spec_set 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 spec_set. + *
> > > + * 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
> > > spec_set 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 spec_set data, and then use + * it as a usual Datum object,
> > > when needed. This Datum should be cached + * somewhere (for example in
> > > rel->rd_options for relations) when object that + * has option is loaded
> > > from db.
> > > + */
> > > +
> > > +
> > > +/* optionsDefListToRawValues
> > > + *        Converts option values that came from syntax analyzer 
> (DefList) into
> > > + *        Values List.
> > > + *
> > > + * No parsing is done here except for checking that RESET syntax is
> > > correct + * (syntax analyzer do not see difference between SET and RESET
> > > cases, we + * should treat it here manually
> > > + */
> > > +static List *
> > > +optionsDefListToRawValues(List *defList, options_parse_mode parse_mode)
> > > +{
> > > +    ListCell   *cell;
> > > +    List       *result = NIL;
> > > +
> > > +    foreach(cell, defList)
> > > +    {
> > > +        option_value *option_dst;
> > > +        DefElem    *def = (DefElem *) lfirst(cell);
> > > +        char       *value;
> > > +
> > > +        option_dst = palloc(sizeof(option_value));
> > > +
> > > +        if (def->defnamespace)
> > > +        {
> > > +            option_dst->namespace = palloc(strlen(def-
> >defnamespace) + 1);
> > > +            strcpy(option_dst->namespace, def->defnamespace);
> > > +        }
> > > +        else
> > > +        {
> > > +            option_dst->namespace = NULL;
> > > +        }
> > > +        option_dst->raw_name = palloc(strlen(def->defname) + 1);
> > > +        strcpy(option_dst->raw_name, def->defname);
> > > +
> > > +        if (parse_mode & OPTIONS_PARSE_MODE_FOR_RESET)
> > > +        {
> > > +            /*
> > > +             * If this option came from RESET statement we should 
> throw error
> > > +             * it it brings us name=value data, as syntax 
> analyzer do not
> > > +             * prevent it
> > > +             */
> > > +            if (def->arg != NULL)
> > > +                ereport(ERROR,
> > > +                        
> (errcode(ERRCODE_SYNTAX_ERROR),
> > > +                    errmsg("RESET must not include values 
> for parameters")));
> > > +
> > > +            option_dst->status = OPTION_VALUE_STATUS_FOR_RESET;
> > > +        }
> > > +        else
> > > +        {
> > > +            /*
> > > +             * For SET statement we should treat (name) 
> expression as if it is
> > > +             * actually (name=true) so do it here manually. In 
> other cases
> > > +             * just use value as we should use it
> > > +             */
> > > +            option_dst->status = OPTION_VALUE_STATUS_RAW;
> > > +            if (def->arg != NULL)
> > > +                value = defGetString(def);
> > > +            else
> > > +                value = "true";
> > > +            option_dst->raw_value = palloc(strlen(value) + 1);
> > > +            strcpy(option_dst->raw_value, value);
> > > +        }
> > > +
> > > +        result = lappend(result, option_dst);
> > > +    }
> > > +    return result;
> > > +}
> > > +
> > > +/*
> > > + * optionsValuesToTextArray
> > > + *        Converts List of option_values into TextArray
> > > + *
> > > + *    Convertation is made to put options into database (e.g. in
> > > + *    pg_class.reloptions for all relation options)
> > > + */
> > > +
> > > +Datum
> > > +optionsValuesToTextArray(List *options_values)
> > > +{
> > > +    ArrayBuildState *astate = NULL;
> > > +    ListCell   *cell;
> > > +    Datum        result;
> > > +
> > > +    foreach(cell, options_values)
> > > +    {
> > > +        option_value *option = (option_value *) lfirst(cell);
> > > +        const char *name;
> > > +        char       *value;
> > > +        text       *t;
> > > +        int            len;
> > > +
> > > +        /*
> > > +         * Raw value were not cleared while parsing, so instead of 
> converting
> > > +         * it back, just use it to store value as text
> > > +         */
> > > +        value = option->raw_value;
> > > +
> > > +        Assert(option->status != OPTION_VALUE_STATUS_EMPTY);
> > > +
> > > +        /*
> > > +         * Name will be taken from option definition, if option were 
> parsed or
> > > +         * from raw_name if option were not parsed for some reason
> > > +         */
> > > +        if (option->status == OPTION_VALUE_STATUS_PARSED)
> > > +            name = option->gen->name;
> > > +        else
> > > +            name = option->raw_name;
> > > +
> > > +        /*
> > > +         * Now build "name=value" string and append it to the array
> > > +         */
> > > +        len = VARHDRSZ + strlen(name) + strlen(value) + 1;
> > > +        t = (text *) palloc(len + 1);
> > > +        SET_VARSIZE(t, len);
> > > +        sprintf(VARDATA(t), "%s=%s", name, value);
> > > +        astate = accumArrayResult(astate, PointerGetDatum(t), false,
> > > +                                  TEXTOID, 
> CurrentMemoryContext);
> > > +    }
> > > +    if (astate)
> > > +        result = makeArrayResult(astate, CurrentMemoryContext);
> > > +    else
> > > +        result = (Datum) 0;
> > > +
> > > +    return result;
> > > +}
> > > +
> > > +/*
> > > + * optionsTextArrayToRawValues
> > > + *        Converts options from TextArray format into RawValues list.
> > > + *
> > > + *    This function is used to convert options data that comes from 
> database
> > > to + *    List of option_values, for further parsing, and, in the case of
> > > ALTER + *    command, for merging with new option values.
> > > + */
> > > +List *
> > > +optionsTextArrayToRawValues(Datum array_datum)
> > > +{
> > > +    List       *result = NIL;
> > > +
> > > +    if (PointerIsValid(DatumGetPointer(array_datum)))
> > > +    {
> > > +        ArrayType  *array = DatumGetArrayTypeP(array_datum);
> > > +        Datum       *options;
> > > +        int            noptions;
> > > +        int            i;
> > > +
> > > +        deconstruct_array(array, TEXTOID, -1, false, 'i',
> > > +                          &options, NULL, &noptions);
> > > +
> > > +        for (i = 0; i < noptions; i++)
> > > +        {
> > > +            option_value *option_dst;
> > > +            char       *text_str = VARDATA(options[i]);
> > > +            int            text_len = 
> VARSIZE(options[i]) - VARHDRSZ;
> > > +            int            i;
> > > +            int            name_len = -1;
> > > +            char       *name;
> > > +            int            raw_value_len;
> > > +            char       *raw_value;
> > > +
> > > +            /*
> > > +             * Find position of '=' sign and treat id as a 
> separator between
> > > +             * name and value in "name=value" item
> > > +             */
> > > +            for (i = 0; i < text_len; i = i + pg_mblen(text_str))
> > > +            {
> > > +                if (text_str[i] == '=')
> > > +                {
> > > +                    name_len = i;
> > > +                    break;
> > > +                }
> > > +            }
> > > +            Assert(name_len >= 1);        /* Just in case 
> */
> > > +
> > > +            raw_value_len = text_len - name_len - 1;
> > > +
> > > +            /*
> > > +             * Copy name from src
> > > +             */
> > > +            name = palloc(name_len + 1);
> > > +            memcpy(name, text_str, name_len);
> > > +            name[name_len] = '\0';
> > > +
> > > +            /*
> > > +             * Copy value from src
> > > +             */
> > > +            raw_value = palloc(raw_value_len + 1);
> > > +            memcpy(raw_value, text_str + name_len + 1, 
> raw_value_len);
> > > +            raw_value[raw_value_len] = '\0';
> > > +
> > > +            /*
> > > +             * Create new option_value item
> > > +             */
> > > +            option_dst = palloc(sizeof(option_value));
> > > +            option_dst->status = OPTION_VALUE_STATUS_RAW;
> > > +            option_dst->raw_name = name;
> > > +            option_dst->raw_value = raw_value;
> > > +            option_dst->namespace = NULL;
> > > +
> > > +            result = lappend(result, option_dst);
> > > +        }
> > > +    }
> > > +    return result;
> > > +}
> > > +
> > > +/*
> > > + * optionsMergeOptionValues
> > > + *        Merges two lists of option_values into one list
> > > + *
> > > + * This function is used to merge two Values list into one. It is used
> > > for all + * kinds of ALTER commands when existing options are
> > > merged|replaced with new + * options list. This function also process
> > > RESET variant of ALTER command. It + * merges two lists as usual, and
> > > then removes all items with RESET flag on. + *
> > > + * Both incoming lists will be destroyed while merging
> > > + */
> > > +static List *
> > > +optionsMergeOptionValues(List *old_options, List *new_options)
> > > +{
> > > +    List       *result = NIL;
> > > +    ListCell   *old_cell;
> > > +    ListCell   *new_cell;
> > > +
> > > +    /*
> > > +     * First add to result all old options that are not mentioned in new
> > > list
> > > +     */
> > > +    foreach(old_cell, old_options)
> > > +    {
> > > +        bool        found;
> > > +        const char *old_name;
> > > +        option_value *old_option;
> > > +
> > > +        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;
> > > +
> > > +        /*
> > > +         * Looking for a new option with same name
> > > +         */
> > > +        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 (strcmp(new_name, old_name) == 0)
> > > +            {
> > > +                found = true;
> > > +                break;
> > > +            }
> > > +        }
> > > +        if (!found)
> > > +            result = lappend(result, old_option);
> > > +    }
> > > +    /*
> > > +     * Now add all to result all new options that are not designated for
> > > reset +     */
> > > +    foreach(new_cell, new_options)
> > > +    {
> > > +        option_value *new_option;
> > > +        new_option = (option_value *) lfirst(new_cell);
> > > +
> > > +        if(new_option->status != OPTION_VALUE_STATUS_FOR_RESET)
> > > +            result = lappend(result, new_option);
> > > +    }
> > > +    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 (strcmp(def->defnamespace,
> > > +                                  
> allowed_namespaces[i]) == 0)
> > > +                {
> > > +                    found = true;
> > > +                    break;
> > > +                }
> > > +                i++;
> > > +            }
> > > +            if (!found)
> > > +                ereport(ERROR,
> > > +                        
> (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > > +                         errmsg("unrecognized 
> parameter namespace \"%s\"",
> > > +                                def-
> >defnamespace)));
> > > +        }
> > > +    }
> > > +}
> > > +
> > > +/*
> > > + * optionsDefListFilterNamespaces
> > > + *        Iterates over DefList, choose items with specified namespace 
> and adds
> > > + *        them to a result List
> > > + *
> > > + * This function does not destroy source DefList but does not create
> > > copies + * of List nodes.
> > > + * It is actually used only for tables, in order to split toast and heap
> > > + * reloptions, so each one can be stored in on it's own pg_class record
> > > + */
> > > +List *
> > > +optionsDefListFilterNamespaces(List *defList, const char *namespace)
> > > +{
> > > +    ListCell   *cell;
> > > +    List       *result = NIL;
> > > +
> > > +    foreach(cell, defList)
> > > +    {
> > > +        DefElem    *def = (DefElem *) lfirst(cell);
> > > +
> > > +        if ((!namespace && !def->defnamespace) ||
> > > +            (namespace && def->defnamespace &&
> > > +             strcmp(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;
> > > +}
> > > +
> > > +/* FIXME write comment here */
> > > +
> > > +Datum
> > > +optionsDefListToTextArray(List *defList)
> > > +{
> > > +    ListCell   *cell;
> > > +    Datum        result;
> > > +    ArrayBuildState *astate = NULL;
> > > +
> > > +    foreach(cell, defList)
> > > +    {
> > > +        DefElem       *def = (DefElem *) lfirst(cell);
> > > +        const char *name = def->defname;
> > > +        const char *value;
> > > +        text       *t;
> > > +        int            len;
> > > +
> > > +        if (def->arg != NULL)
> > > +            value = defGetString(def);
> > > +        else
> > > +            value = "true";
> > > +
> > > +        if (def->defnamespace)
> > > +        {
> > > +            Assert(false); /* Should not get here */
> > > +            /* This function is used for backward compatibility 
> in the place were
> > > namespases are not allowed */ +            return (Datum) 0;
> > > +        }
> > > +        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;
> > > +}
> > > +
> > > +
> > > +/*
> > > + * 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_spec_set 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_spec_set 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_spec_set * spec_set,
> > > +                      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) * spec_set->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 && (!spec_set->namespace ||
> > > +                  strcmp(spec_set->namespace, option-
> >namespace) != 0))
> > > +        {
> > > +            ereport(ERROR,
> > > +                    
> (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > > +                     errmsg("unrecognized parameter 
> namespace \"%s\"",
> > > +                            option->namespace)));
> > > +        }
> > > +
> > > +        for (i = 0; i < spec_set->num; i++)
> > > +        {
> > > +            option_spec_basic *definition = spec_set-
> >definitions[i];
> > > +
> > > +            if (strcmp(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 
> spec_set. 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)));
> > > +            } else
> > > +            {
> > > +                /* RESET is always in non-validating mode, 
> unkown names should
> > > +                 * be ignored. This is traditional behaviour 
> of postgres/
> > > +                 * FIXME may be it should be changed someday
> > > +                 */
> > > +                if (option->status == 
> OPTION_VALUE_STATUS_FOR_RESET)
> > > +                {
> > > +                    skip = true;
> > > +                }
> > > +            }
> > > +            /*
> > > +             * In other cases, if we are parsing not in validate 
> mode, then
> > > +             * we should keep unknown node, because non-validate 
> mode is for
> > > +             * data that is already in the DB and should not be 
> changed after
> > > +             * altering another entries
> > > +             */
> > > +        }
> > > +        if (!skip)
> > > +            result = lappend(result, option);
> > > +    }
> > > +    return result;
> > > +}
> > > +
> > > +/*
> > > + * parse_one_option
> > > + *
> > > + *        Subroutine for optionsParseRawValues, to parse and validate 
> a
> > > + *        single option's value
> > > + */
> > > +static void
> > > +parse_one_option(option_value * option, const char *text_str, int
> > > text_len, +                 bool validate)
> > > +{
> > > +    char       *value;
> > > +    bool        parsed;
> > > +
> > > +    value = option->raw_value;
> > > +
> > > +    switch (option->gen->type)
> > > +    {
> > > +        case OPTION_TYPE_BOOL:
> > > +            {
> > > +                parsed = parse_bool(value, &option-
> >values.bool_val);
> > > +                if (validate && !parsed)
> > > +                    ereport(ERROR,
> > > +                            
> (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > > +                        errmsg("invalid value for 
> boolean option \"%s\": %s",
> > > +                               option->gen->name, 
> value)));
> > > +            }
> > > +            break;
> > > +        case OPTION_TYPE_INT:
> > > +            {
> > > +                option_spec_int *optint =
> > > +                (option_spec_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_spec_real *optreal =
> > > +                (option_spec_real *) option->gen;
> > > +
> > > +                parsed = parse_real(value, &option-
> >values.real_val, 0, NULL);
> > > +                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_spec_enum *optenum =
> > > +                                        
> (option_spec_enum *) option->gen;
> > > +                opt_enum_elt_def *elt;
> > > +                parsed = false;
> > > +                for (elt = optenum->members; elt->string_val; 
> elt++)
> > > +                {
> > > +                    if (strcmp(value, elt->string_val) == 
> 0)
> > > +                    {
> > > +                        option->values.enum_val = 
> elt->symbol_val;
> > > +                        parsed = true;
> > > +                        break;
> > > +                    }
> > > +                }
> > > +                if (!parsed)
> > > +                {
> > > +                    ereport(ERROR,
> > > +
> (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > > +                             errmsg("invalid 
> value for enum option \"%s\": %s",
> > > +                                    
> option->gen->name, value),
> > > +                             optenum->detailmsg ?
> > > +                             
> errdetail_internal("%s", _(optenum->detailmsg)) : 0));
> > > +                }
> > > +            }
> > > +            break;
> > > +        case OPTION_TYPE_STRING:
> > > +            {
> > > +                option_spec_string *optstring =
> > > +                (option_spec_string *) option->gen;
> > > +
> > > +                option->values.string_val = value;
> > > +                if (validate && optstring->validate_cb)
> > > +                    (optstring->validate_cb) (value);
> > > +                parsed = true;
> > > +            }
> > > +            break;
> > > +        default:
> > > +            elog(ERROR, "unsupported reloption type %d", option-
> >gen->type);
> > > +            parsed = true;        /* quiet compiler */
> > > +            break;
> > > +    }
> > > +
> > > +    if (parsed)
> > > +        option->status = OPTION_VALUE_STATUS_PARSED;
> > > +
> > > +}
> > > +
> > > +/*
> > > + * optionsAllocateBytea
> > > + *        Allocates memory for bytea options representation
> > > + *
> > > + * Function allocates memory for byrea structure of an option, plus adds
> > > space + * for values of string options. We should keep all data including
> > > string + * values in the same memory chunk, because Cache code copies
> > > bytea option + * data from one MemoryConext to another without knowing
> > > about it's internal + * structure, so it would not be able to copy string
> > > values if they are outside + * of bytea memory chunk.
> > > + */
> > > +static void *
> > > +optionsAllocateBytea(options_spec_set * spec_set, List *options)
> > > +{
> > > +    Size        size;
> > > +    int            i;
> > > +    ListCell   *cell;
> > > +    int            length;
> > > +    void       *res;
> > > +
> > > +    size = spec_set->struct_size;
> > > +
> > > +    /* Calculate size needed to store all string values for this option 
> */
> > > +    for (i = 0; i < spec_set->num; i++)
> > > +    {
> > > +        option_spec_basic *definition = spec_set->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 
> spec_set
> > > +         * entry
> > > +         */
> > > +        foreach(cell, options)
> > > +        {
> > > +            option = (option_value *) lfirst(cell);
> > > +            if (option->status == OPTION_VALUE_STATUS_PARSED &&
> > > +                strcmp(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_spec_string *) definition)->default_val)
> > > +            length = strlen(
> > > +                 ((option_spec_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_spec_set: each spec_set item
> > > + * has offset value, and option value in binary mode is written to the
> > > + * structure with that offset.
> > > + *
> > > + * More special case is string values. Memory for bytea structure is
> > > allocated + * by optionsAllocateBytea which adds some more space for
> > > string values to + * the size of original structure. All string values
> > > are copied there and + * inside the bytea structure an offset to that
> > > value is kept.
> > > + *
> > > + */
> > > +static bytea *
> > > +optionsValuesToBytea(List *options, options_spec_set * spec_set)
> > > +{
> > > +    char       *data;
> > > +    char       *string_values_buffer;
> > > +    int            i;
> > > +
> > > +    data = optionsAllocateBytea(spec_set, options);
> > > +
> > > +    /* place for string data starts right after original structure */
> > > +    string_values_buffer = data + spec_set->struct_size;
> > > +
> > > +    for (i = 0; i < spec_set->num; i++)
> > > +    {
> > > +        option_value *found = NULL;
> > > +        ListCell   *cell;
> > > +        char       *item_pos;
> > > +        option_spec_basic *definition = spec_set->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 (strcmp(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_spec_bool *) definition)-
> >default_val;
> > > +                break;
> > > +            case OPTION_TYPE_INT:
> > > +                *(int *) item_pos = found ?
> > > +                    found->values.int_val :
> > > +                    ((option_spec_int *) definition)-
> >default_val;
> > > +                break;
> > > +            case OPTION_TYPE_REAL:
> > > +                *(double *) item_pos = found ?
> > > +                    found->values.real_val :
> > > +                    ((option_spec_real *) definition)-
> >default_val;
> > > +                break;
> > > +            case OPTION_TYPE_ENUM:
> > > +                *(int *) item_pos = found ?
> > > +                    found->values.enum_val :
> > > +                    ((option_spec_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_spec_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_spec_set * spec_set, 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);
> > > +    if (! (parse_mode & OPTIONS_PARSE_MODE_FOR_RESET))
> > > +    {
> > > +        /* FIXME: postgres usual behaviour vas not to vaidate names 
> that
> > > +         * came from RESET command. Once this behavious should be 
> changed,
> > > +         * I guess. But for now we keep it as it was.
> > > +         */
> > > +        parse_mode|= OPTIONS_PARSE_MODE_VALIDATE;
> > > +    }
> > > +    new_values = optionsParseRawValues(new_values, spec_set, parse_mode);
> > > +
> > > +    /*
> > > +     * 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, 
> spec_set, 0);
> > > +    }
> > > +    else
> > > +    {
> > > +        merged_values = new_values;
> > > +    }
> > > +
> > > +    /*
> > > +     * If we have postprocess_fun function defined in spec_set, 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 (spec_set->postprocess_fun)
> > > +    {
> > > +        bytea       *data = optionsValuesToBytea(merged_values, 
> spec_set);
> > > +
> > > +        spec_set->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_spec_set * spec_set, Datum data, bool
> > > validate) +{
> > > +    List       *values;
> > > +    bytea       *options;
> > > +
> > > +    values = optionsTextArrayToRawValues(data);
> > > +    values = optionsParseRawValues(values, spec_set,
> > > +                                validate ? 
> OPTIONS_PARSE_MODE_VALIDATE : 0);
> > > +    options = optionsValuesToBytea(values, spec_set);
> > > +
> > > +    if (spec_set->postprocess_fun)
> > > +    {
> > > +        spec_set->postprocess_fun(options, false);
> > > +    }
> > > +    return options;
> > > +}
> > > diff --git a/src/backend/access/common/relation.c
> > > b/src/backend/access/common/relation.c index 632d13c..49ad197 100644
> > > --- a/src/backend/access/common/relation.c
> > > +++ b/src/backend/access/common/relation.c
> > > @@ -65,9 +65,13 @@ relation_open(Oid relationId, LOCKMODE lockmode)
> > > 
> > >       * If we didn't get the lock ourselves, assert that caller holds 
> one,
> > >       * except in bootstrap mode where no locks are used.
> > >       */
> > > 
> > > -    Assert(lockmode != NoLock ||
> > > -           IsBootstrapProcessingMode() ||
> > > -           CheckRelationLockedByMe(r, AccessShareLock, true));
> > > +
> > > +// FIXME We need NoLock mode to get AM data when choosing Lock for
> > > +// attoptions is changed. See ProcessUtilitySlow problems comes from
> > > there
> > > +// This is a dirty hack, we need better solution for this case;
> > > +//    Assert(lockmode != NoLock ||
> > > +//           IsBootstrapProcessingMode() ||
> > > +//           CheckRelationLockedByMe(r, AccessShareLock, true));
> > > 
> > >      /* Make note that we've accessed a temporary relation */
> > >      if (RelationUsesLocalBuffers(r))
> > > 
> > > diff --git a/src/backend/access/common/reloptions.c
> > > b/src/backend/access/common/reloptions.c index b5602f5..29ab98a 100644
> > > --- a/src/backend/access/common/reloptions.c
> > > +++ b/src/backend/access/common/reloptions.c
> > > @@ -1,7 +1,7 @@
> > > 
> > >  /*-----------------------------------------------------------------------
> > >  --
> > >  
> > >   *
> > >   * reloptions.c
> > > 
> > > - *      Core support for relation options (pg_class.reloptions)
> > > + *      Support for relation options (pg_class.reloptions)
> > > 
> > >   *
> > >   * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
> > >   * Portions Copyright (c) 1994, Regents of the University of California
> > > 
> > > @@ -17,13 +17,10 @@
> > > 
> > >  #include <float.h>
> > > 
> > > -#include "access/gist_private.h"
> > > -#include "access/hash.h"
> > > 
> > >  #include "access/heaptoast.h"
> > >  #include "access/htup_details.h"
> > > 
> > > -#include "access/nbtree.h"
> > > 
> > >  #include "access/reloptions.h"
> > > 
> > > -#include "access/spgist_private.h"
> > > +#include "access/options.h"
> > > 
> > >  #include "catalog/pg_type.h"
> > >  #include "commands/defrem.h"
> > >  #include "commands/tablespace.h"
> > > 
> > > @@ -36,6 +33,7 @@
> > > 
> > >  #include "utils/guc.h"
> > >  #include "utils/memutils.h"
> > >  #include "utils/rel.h"
> > > 
> > > +#include "storage/bufmgr.h"
> > > 
> > >  /*
> > >  
> > >   * Contents of pg_class.reloptions
> > > 
> > > @@ -93,380 +91,8 @@
> > > 
> > >   * value has no effect until the next VACUUM, so no need for stronger
> > >   lock.
> > >   */
> > > 
> > > -static relopt_bool boolRelOpts[] =
> > > -{
> > > -    {
> > > -        {
> > > -            "autosummarize",
> > > -            "Enables automatic summarization on this BRIN 
> index",
> > > -            RELOPT_KIND_BRIN,
> > > -            AccessExclusiveLock
> > > -        },
> > > -        false
> > > -    },
> > > -    {
> > > -        {
> > > -            "autovacuum_enabled",
> > > -            "Enables autovacuum in this relation",
> > > -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > > -            ShareUpdateExclusiveLock
> > > -        },
> > > -        true
> > > -    },
> > > -    {
> > > -        {
> > > -            "user_catalog_table",
> > > -            "Declare a table as an additional catalog table, 
> e.g. for the purpose
> > > of logical replication", -            RELOPT_KIND_HEAP,
> > > -            AccessExclusiveLock
> > > -        },
> > > -        false
> > > -    },
> > > -    {
> > > -        {
> > > -            "fastupdate",
> > > -            "Enables \"fast update\" feature for this GIN 
> index",
> > > -            RELOPT_KIND_GIN,
> > > -            AccessExclusiveLock
> > > -        },
> > > -        true
> > > -    },
> > > -    {
> > > -        {
> > > -            "security_barrier",
> > > -            "View acts as a row security barrier",
> > > -            RELOPT_KIND_VIEW,
> > > -            AccessExclusiveLock
> > > -        },
> > > -        false
> > > -    },
> > > -    {
> > > -        {
> > > -            "vacuum_truncate",
> > > -            "Enables vacuum to truncate empty pages at the end 
> of this table",
> > > -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > > -            ShareUpdateExclusiveLock
> > > -        },
> > > -        true
> > > -    },
> > > -    {
> > > -        {
> > > -            "deduplicate_items",
> > > -            "Enables \"deduplicate items\" feature for this 
> btree index",
> > > -            RELOPT_KIND_BTREE,
> > > -            ShareUpdateExclusiveLock    /* since it applies only 
> to later
> > > -                                        
>  * inserts */
> > > -        },
> > > -        true
> > > -    },
> > > -    /* 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_vacuum_insert_threshold",
> > > -            "Minimum number of tuple inserts prior to vacuum, or 
> -1 to disable
> > > insert vacuums", -            RELOPT_KIND_HEAP | 
> RELOPT_KIND_TOAST,
> > > -            ShareUpdateExclusiveLock
> > > -        },
> > > -        -2, -1, 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_limit",
> > > -            "Vacuum cost amount available before napping, for 
> autovacuum",
> > > -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > > -            ShareUpdateExclusiveLock
> > > -        },
> > > -        -1, 1, 10000
> > > -    },
> > > -    {
> > > -        {
> > > -            "autovacuum_freeze_min_age",
> > > -            "Minimum age at which VACUUM should freeze a table 
> row, for
> > > autovacuum", -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > > -            ShareUpdateExclusiveLock
> > > -        },
> > > -        -1, 0, 1000000000
> > > -    },
> > > -    {
> > > -        {
> > > -            "autovacuum_multixact_freeze_min_age",
> > > -            "Minimum multixact age at which VACUUM should freeze 
> a row
> > > multixact's, for autovacuum", -            RELOPT_KIND_HEAP | 
> RELOPT_KIND_TOAST,
> > > -            ShareUpdateExclusiveLock
> > > -        },
> > > -        -1, 0, 1000000000
> > > -    },
> > > -    {
> > > -        {
> > > -            "autovacuum_freeze_max_age",
> > > -            "Age at which to autovacuum a table to prevent 
> transaction ID
> > > wraparound", -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > > -            ShareUpdateExclusiveLock
> > > -        },
> > > -        -1, 100000, 2000000000
> > > -    },
> > > -    {
> > > -        {
> > > -            "autovacuum_multixact_freeze_max_age",
> > > -            "Multixact age at which to autovacuum a table to 
> prevent multixact
> > > wraparound", -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > > -            ShareUpdateExclusiveLock
> > > -        },
> > > -        -1, 10000, 2000000000
> > > -    },
> > > -    {
> > > -        {
> > > -            "autovacuum_freeze_table_age",
> > > -            "Age at which VACUUM should perform a full table 
> sweep to freeze row
> > > versions", -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > > -            ShareUpdateExclusiveLock
> > > -        }, -1, 0, 2000000000
> > > -    },
> > > -    {
> > > -        {
> > > -            "autovacuum_multixact_freeze_table_age",
> > > -            "Age of multixact at which VACUUM should perform a 
> full table sweep to
> > > freeze row versions", -            RELOPT_KIND_HEAP | 
> RELOPT_KIND_TOAST,
> > > -            ShareUpdateExclusiveLock
> > > -        }, -1, 0, 2000000000
> > > -    },
> > > -    {
> > > -        {
> > > -            "log_autovacuum_min_duration",
> > > -            "Sets the minimum execution time above which 
> autovacuum actions will
> > > be logged", -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > > -            ShareUpdateExclusiveLock
> > > -        },
> > > -        -1, -1, INT_MAX
> > > -    },
> > > -    {
> > > -        {
> > > -            "toast_tuple_target",
> > > -            "Sets the target tuple length at which external 
> columns will be
> > > toasted", -            RELOPT_KIND_HEAP,
> > > -            ShareUpdateExclusiveLock
> > > -        },
> > > -        TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
> > > -    },
> > > -    {
> > > -        {
> > > -            "pages_per_range",
> > > -            "Number of pages that each page range covers in a 
> BRIN index",
> > > -            RELOPT_KIND_BRIN,
> > > -            AccessExclusiveLock
> > > -        }, 128, 1, 131072
> > > -    },
> > > -    {
> > > -        {
> > > -            "gin_pending_list_limit",
> > > -            "Maximum size of the pending list for this GIN 
> index, in kilobytes.",
> > > -            RELOPT_KIND_GIN,
> > > -            AccessExclusiveLock
> > > -        },
> > > -        -1, 64, MAX_KILOBYTES
> > > -    },
> > > -    {
> > > -        {
> > > -            "effective_io_concurrency",
> > > -            "Number of simultaneous requests that can be handled 
> efficiently by
> > > the disk subsystem.", -            RELOPT_KIND_TABLESPACE,
> > > -            ShareUpdateExclusiveLock
> > > -        },
> > > -#ifdef USE_PREFETCH
> > > -        -1, 0, MAX_IO_CONCURRENCY
> > > -#else
> > > -        0, 0, 0
> > > -#endif
> > > -    },
> > > -    {
> > > -        {
> > > -            "maintenance_io_concurrency",
> > > -            "Number of simultaneous requests that can be handled 
> efficiently by
> > > the disk subsystem for maintenance work.", -            
> 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_cost_delay",
> > > -            "Vacuum cost delay in milliseconds, for autovacuum",
> > > -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > > -            ShareUpdateExclusiveLock
> > > -        },
> > > -        -1, 0.0, 100.0
> > > -    },
> > > -    {
> > > -        {
> > > -            "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_vacuum_insert_scale_factor",
> > > -            "Number of tuple inserts 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
> > > -    },
> > > -    {
> > > -        {
> > > -            "vacuum_cleanup_index_scale_factor",
> > > -            "Deprecated B-Tree parameter.",
> > > -            RELOPT_KIND_BTREE,
> > > -            ShareUpdateExclusiveLock
> > > -        },
> > > -        -1, 0.0, 1e10
> > > -    },
> > > -    /* list terminator */
> > > -    {{NULL}}
> > > -};
> > > -
> > > 
> > >  /* values from StdRdOptIndexCleanup */
> > > 
> > > -relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
> > > +opt_enum_elt_def StdRdOptIndexCleanupValues[] =
> > > 
> > >  {
> > >  
> > >      {"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
> > >      {"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
> > > 
> > > @@ -480,17 +106,8 @@ relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
> > > 
> > >      {(const char *) NULL}        /* list terminator */
> > >  
> > >  };
> > > 
> > > -/* values from GistOptBufferingMode */
> > > -relopt_enum_elt_def gistBufferingOptValues[] =
> > > -{
> > > -    {"auto", GIST_OPTION_BUFFERING_AUTO},
> > > -    {"on", GIST_OPTION_BUFFERING_ON},
> > > -    {"off", GIST_OPTION_BUFFERING_OFF},
> > > -    {(const char *) NULL}        /* list terminator */
> > > -};
> > > -
> > > 
> > >  /* values from ViewOptCheckOption */
> > > 
> > > -relopt_enum_elt_def viewCheckOptValues[] =
> > > +opt_enum_elt_def viewCheckOptValues[] =
> > > 
> > >  {
> > >  
> > >      /* no value for NOT_SET */
> > >      {"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
> > > 
> > > @@ -498,61 +115,8 @@ relopt_enum_elt_def viewCheckOptValues[] =
> > > 
> > >      {(const char *) NULL}        /* list terminator */
> > >  
> > >  };
> > > 
> > > -static relopt_enum enumRelOpts[] =
> > > -{
> > > -    {
> > > -        {
> > > -            "vacuum_index_cleanup",
> > > -            "Controls index vacuuming and index cleanup",
> > > -            RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
> > > -            ShareUpdateExclusiveLock
> > > -        },
> > > -        StdRdOptIndexCleanupValues,
> > > -        STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO,
> > > -        gettext_noop("Valid values are \"on\", \"off\", and 
> \"auto\".")
> > > -    },
> > > -    {
> > > -        {
> > > -            "buffering",
> > > -            "Enables buffering build for this GiST index",
> > > -            RELOPT_KIND_GIST,
> > > -            AccessExclusiveLock
> > > -        },
> > > -        gistBufferingOptValues,
> > > -        GIST_OPTION_BUFFERING_AUTO,
> > > -        gettext_noop("Valid values are \"on\", \"off\", and 
> \"auto\".")
> > > -    },
> > > -    {
> > > -        {
> > > -            "check_option",
> > > -            "View has WITH CHECK OPTION defined (local or 
> cascaded).",
> > > -            RELOPT_KIND_VIEW,
> > > -            AccessExclusiveLock
> > > -        },
> > > -        viewCheckOptValues,
> > > -        VIEW_OPTION_CHECK_OPTION_NOT_SET,
> > > -        gettext_noop("Valid values are \"local\" and \"cascaded\".")
> > > -    },
> > > -    /* list terminator */
> > > -    {{NULL}}
> > > -};
> > > -
> > > -static relopt_string stringRelOpts[] =
> > > -{
> > > -    /* 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);
> > > +options_spec_set *get_stdrd_relopt_spec_set(relopt_kind kind);
> > > 
> > >  /*
> > >  
> > >   * Get the length of a string reloption (either default or the
> > >   user-defined
> > > 
> > > @@ -563,160 +127,6 @@ static void parse_one_reloption(relopt_value
> > > *option, char *text_str,> 
> > >      ((option).isset ? strlen((option).values.string_val) : \
> > >      
> > >       ((relopt_string *) (option).gen)->default_len)
> > > 
> > > -/*
> > > - * 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; enumRelOpts[i].gen.name; i++)
> > > -    {
> > > -        Assert(DoLockModesConflict(enumRelOpts[i].gen.lockmode,
> > > -                                   
> enumRelOpts[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; enumRelOpts[i].gen.name; i++)
> > > -    {
> > > -        relOpts[j] = &enumRelOpts[i].gen;
> > > -        relOpts[j]->type = RELOPT_TYPE_ENUM;
> > > -        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;
> > > -}
> > > 
> > >  /*
> > >  
> > >   * init_local_reloptions
> > > 
> > > @@ -729,6 +139,7 @@ init_local_reloptions(local_relopts *opts, Size
> > > relopt_struct_size)> 
> > >      opts->options = NIL;
> > >      opts->validators = NIL;
> > >      opts->relopt_struct_size = relopt_struct_size;
> > > 
> > > +    opts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, 0);
> > > 
> > >  }
> > >  
> > >  /*
> > > 
> > > @@ -743,112 +154,6 @@ register_reloptions_validator(local_relopts *opts,
> > > relopts_validator validator)> 
> > >  }
> > >  
> > >  /*
> > > 
> > > - * add_local_reloption
> > > - *        Add an already-created custom reloption to the local list.
> > > - */
> > > -static void
> > > -add_local_reloption(local_relopts *relopts, relopt_gen *newoption, int
> > > offset) -{
> > > -    local_relopt *opt = palloc(sizeof(*opt));
> > > -
> > > -    Assert(offset < relopts->relopt_struct_size);
> > > -
> > > -    opt->option = newoption;
> > > -    opt->offset = offset;
> > > -
> > > -    relopts->options = lappend(relopts->options, opt);
> > > -}
> > > -
> > > -/*
> > > - * allocate_reloption
> > > - *        Allocate a new reloption and initialize the type-agnostic 
> fields
> > > - *        (for types other than string)
> > > - */
> > > -static relopt_gen *
> > > -allocate_reloption(bits32 kinds, int type, const char *name, const char
> > > *desc, -                   LOCKMODE lockmode)
> > > -{
> > > -    MemoryContext oldcxt;
> > > -    size_t        size;
> > > -    relopt_gen *newoption;
> > > -
> > > -    if (kinds != RELOPT_KIND_LOCAL)
> > > -        oldcxt = MemoryContextSwitchTo(TopMemoryContext);
> > > -    else
> > > -        oldcxt = NULL;
> > > -
> > > -    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_ENUM:
> > > -            size = sizeof(relopt_enum);
> > > -            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;
> > > -    newoption->lockmode = lockmode;
> > > -
> > > -    if (oldcxt != NULL)
> > > -        MemoryContextSwitchTo(oldcxt);
> > > -
> > > -    return newoption;
> > > -}
> > > -
> > > -/*
> > > - * init_bool_reloption
> > > - *        Allocate and initialize a new boolean reloption
> > > - */
> > > -static relopt_bool *
> > > -init_bool_reloption(bits32 kinds, const char *name, const char *desc,
> > > -                    bool default_val, LOCKMODE lockmode)
> > > -{
> > > -    relopt_bool *newoption;
> > > -
> > > -    newoption = (relopt_bool *) allocate_reloption(kinds, 
> RELOPT_TYPE_BOOL,
> > > -                                        
>            name, desc, lockmode);
> > > -    newoption->default_val = default_val;
> > > -
> > > -    return newoption;
> > > -}
> > > -
> > > -/*
> > > - * add_bool_reloption
> > > - *        Add a new boolean reloption
> > > - */
> > > -void
> > > -add_bool_reloption(bits32 kinds, const char *name, const char *desc,
> > > -                   bool default_val, LOCKMODE lockmode)
> > > -{
> > > -    relopt_bool *newoption = init_bool_reloption(kinds, name, desc,
> > > -                                        
>          default_val, lockmode);
> > > -
> > > -    add_reloption((relopt_gen *) newoption);
> > > -}
> > > -
> > > -/*
> > > 
> > >   * add_local_bool_reloption
> > >   *        Add a new boolean local reloption
> > >   *
> > > 
> > > @@ -858,47 +163,8 @@ void
> > > 
> > >  add_local_bool_reloption(local_relopts *relopts, const char *name,
> > >  
> > >                           const char *desc, bool 
> default_val, int offset)
> > >  
> > >  {
> > > 
> > > -    relopt_bool *newoption = init_bool_reloption(RELOPT_KIND_LOCAL,
> > > -                                        
>          name, desc,
> > > -                                        
>          default_val, 0);
> > > -
> > > -    add_local_reloption(relopts, (relopt_gen *) newoption, offset);
> > > -}
> > > -
> > > -
> > > -/*
> > > - * init_real_reloption
> > > - *        Allocate and initialize a new integer reloption
> > > - */
> > > -static relopt_int *
> > > -init_int_reloption(bits32 kinds, const char *name, const char *desc,
> > > -                   int default_val, int min_val, int 
> max_val,
> > > -                   LOCKMODE lockmode)
> > > -{
> > > -    relopt_int *newoption;
> > > -
> > > -    newoption = (relopt_int *) allocate_reloption(kinds, 
> RELOPT_TYPE_INT,
> > > -                                        
>           name, desc, lockmode);
> > > -    newoption->default_val = default_val;
> > > -    newoption->min = min_val;
> > > -    newoption->max = max_val;
> > > -
> > > -    return newoption;
> > > -}
> > > -
> > > -/*
> > > - * add_int_reloption
> > > - *        Add a new integer reloption
> > > - */
> > > -void
> > > -add_int_reloption(bits32 kinds, const char *name, const char *desc, int
> > > default_val, -                  int min_val, int max_val, 
> LOCKMODE lockmode)
> > > -{
> > > -    relopt_int *newoption = init_int_reloption(kinds, name, desc,
> > > -                                        
>        default_val, min_val,
> > > -                                        
>        max_val, lockmode);
> > > -
> > > -    add_reloption((relopt_gen *) newoption);
> > > +    optionsSpecSetAddBool(relopts->spec_set, name, desc, NoLock, 0, 
> offset,
> > > +                                        
>                         default_val);
> > > 
> > >  }
> > >  
> > >  /*
> > > 
> > > @@ -912,47 +178,8 @@ add_local_int_reloption(local_relopts *relopts, const
> > > char *name,> 
> > >                          const char *desc, int 
> default_val, int min_val,
> > >                          int max_val, int offset)
> > >  
> > >  {
> > > 
> > > -    relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
> > > -                                        
>        name, desc, default_val,
> > > -                                        
>        min_val, max_val, 0);
> > > -
> > > -    add_local_reloption(relopts, (relopt_gen *) newoption, offset);
> > > -}
> > > -
> > > -/*
> > > - * init_real_reloption
> > > - *        Allocate and initialize a new real reloption
> > > - */
> > > -static relopt_real *
> > > -init_real_reloption(bits32 kinds, const char *name, const char *desc,
> > > -                    double default_val, double min_val, 
> double max_val,
> > > -                    LOCKMODE lockmode)
> > > -{
> > > -    relopt_real *newoption;
> > > -
> > > -    newoption = (relopt_real *) allocate_reloption(kinds, 
> RELOPT_TYPE_REAL,
> > > -                                        
>            name, desc, lockmode);
> > > -    newoption->default_val = default_val;
> > > -    newoption->min = min_val;
> > > -    newoption->max = max_val;
> > > -
> > > -    return newoption;
> > > -}
> > > -
> > > -/*
> > > - * add_real_reloption
> > > - *        Add a new float reloption
> > > - */
> > > -void
> > > -add_real_reloption(bits32 kinds, const char *name, const char *desc,
> > > -                   double default_val, double min_val, 
> double max_val,
> > > -                   LOCKMODE lockmode)
> > > -{
> > > -    relopt_real *newoption = init_real_reloption(kinds, name, desc,
> > > -                                        
>          default_val, min_val,
> > > -                                        
>          max_val, lockmode);
> > > -
> > > -    add_reloption((relopt_gen *) newoption);
> > > +    optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, 0, offset,
> > > +                                        
>         default_val, min_val, max_val);
> > > 
> > >  }
> > >  
> > >  /*
> > > 
> > > @@ -966,57 +193,9 @@ add_local_real_reloption(local_relopts *relopts,
> > > const char *name,> 
> > >                           const char *desc, double 
> default_val,
> > >                           double min_val, double 
> max_val, int offset)
> > >  
> > >  {
> > > 
> > > -    relopt_real *newoption = init_real_reloption(RELOPT_KIND_LOCAL,
> > > -                                        
>          name, desc,
> > > -                                        
>          default_val, min_val,
> > > -                                        
>          max_val, 0);
> > > -
> > > -    add_local_reloption(relopts, (relopt_gen *) newoption, offset);
> > > -}
> > > -
> > > -/*
> > > - * init_enum_reloption
> > > - *        Allocate and initialize a new enum reloption
> > > - */
> > > -static relopt_enum *
> > > -init_enum_reloption(bits32 kinds, const char *name, const char *desc,
> > > -                    relopt_enum_elt_def *members, int 
> default_val,
> > > -                    const char *detailmsg, LOCKMODE 
> lockmode)
> > > -{
> > > -    relopt_enum *newoption;
> > > -
> > > -    newoption = (relopt_enum *) allocate_reloption(kinds, 
> RELOPT_TYPE_ENUM,
> > > -                                        
>            name, desc, lockmode);
> > > -    newoption->members = members;
> > > -    newoption->default_val = default_val;
> > > -    newoption->detailmsg = detailmsg;
> > > -
> > > -    return newoption;
> > > -}
> > > -
> > > -
> > > -/*
> > > - * add_enum_reloption
> > > - *        Add a new enum reloption
> > > - *
> > > - * The members array must have a terminating NULL entry.
> > > - *
> > > - * The detailmsg is shown when unsupported values are passed, and has
> > > this
> > > - * form:   "Valid values are \"foo\", \"bar\", and \"bar\"."
> > > - *
> > > - * The members array and detailmsg are not copied -- caller must ensure
> > > that - * they are valid throughout the life of the process.
> > > - */
> > > -void
> > > -add_enum_reloption(bits32 kinds, const char *name, const char *desc,
> > > -                   relopt_enum_elt_def *members, int 
> default_val,
> > > -                   const char *detailmsg, LOCKMODE lockmode)
> > > -{
> > > -    relopt_enum *newoption = init_enum_reloption(kinds, name, desc,
> > > -                                        
>          members, default_val,
> > > -                                        
>          detailmsg, lockmode);
> > > +    optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, 0, 
> offset,
> > > +                                        
>         default_val, min_val, max_val);
> > > 
> > > -    add_reloption((relopt_gen *) newoption);
> > > 
> > >  }
> > >  
> > >  /*
> > > 
> > > @@ -1027,77 +206,11 @@ add_enum_reloption(bits32 kinds, const char *name,
> > > const char *desc,> 
> > >   */
> > >  
> > >  void
> > >  add_local_enum_reloption(local_relopts *relopts, const char *name,
> > > 
> > > -                         const char *desc, 
> relopt_enum_elt_def *members,
> > > +                         const char *desc, 
> opt_enum_elt_def *members,
> > > 
> > >                           int default_val, const char 
> *detailmsg, int offset)
> > >  
> > >  {
> > > 
> > > -    relopt_enum *newoption = init_enum_reloption(RELOPT_KIND_LOCAL,
> > > -                                        
>          name, desc,
> > > -                                        
>          members, default_val,
> > > -                                        
>          detailmsg, 0);
> > > -
> > > -    add_local_reloption(relopts, (relopt_gen *) newoption, offset);
> > > -}
> > > -
> > > -/*
> > > - * init_string_reloption
> > > - *        Allocate and initialize a new string reloption
> > > - */
> > > -static relopt_string *
> > > -init_string_reloption(bits32 kinds, const char *name, const char *desc,
> > > -                      const char *default_val,
> > > -                      validate_string_relopt validator,
> > > -                      fill_string_relopt filler,
> > > -                      LOCKMODE lockmode)
> > > -{
> > > -    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, lockmode);
> > > -    newoption->validate_cb = validator;
> > > -    newoption->fill_cb = filler;
> > > -    if (default_val)
> > > -    {
> > > -        if (kinds == RELOPT_KIND_LOCAL)
> > > -            newoption->default_val = strdup(default_val);
> > > -        else
> > > -            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;
> > > -    }
> > > -
> > > -    return newoption;
> > > -}
> > > -
> > > -/*
> > > - * add_string_reloption
> > > - *        Add a new string reloption
> > > - *
> > > - * "validator" is an optional function pointer that can be used to test
> > > the - * validity of the values.  It must elog(ERROR) when the argument
> > > string is - * not acceptable for the variable.  Note that the default
> > > value must pass - * the validation.
> > > - */
> > > -void
> > > -add_string_reloption(bits32 kinds, const char *name, const char *desc,
> > > -                     const char *default_val, 
> validate_string_relopt validator,
> > > -                     LOCKMODE lockmode)
> > > -{
> > > -    relopt_string *newoption = init_string_reloption(kinds, name, desc,
> > > -                                        
>              default_val,
> > > -                                        
>              validator, NULL,
> > > -                                        
>              lockmode);
> > > -
> > > -    add_reloption((relopt_gen *) newoption);
> > > +    optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, 0, 
> offset,
> > > +                                        
>     members, default_val, detailmsg);
> > > 
> > >  }
> > >  
> > >  /*
> > > 
> > > @@ -1113,249 +226,9 @@ add_local_string_reloption(local_relopts *relopts,
> > > const char *name,> 
> > >                             validate_string_relopt 
> validator,
> > >                             fill_string_relopt filler, 
> int offset)
> > >  
> > >  {
> > > 
> > > -    relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
> > > -                                        
>              name, desc,
> > > -                                        
>              default_val,
> > > -                                        
>              validator, filler,
> > > -                                        
>              0);
> > > -
> > > -    add_local_reloption(relopts, (relopt_gen *) newoption, offset);
> > > -}
> > > -
> > > -/*
> > > - * 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 acceptOidsOff is true, then we allow oids = false, but throw error
> > > when - * on. This is solely needed for backwards compatibility.
> > > - *
> > > - * Note that this is not responsible for determining whether the options
> > > - * are valid, but it does check that namespaces for all the options given
> > > are - * listed in validnsps.  The NULL namespace is always valid and need
> > > not be - * explicitly listed.  Passing a NULL pointer means that only the
> > > NULL - * namespace is valid.
> > > - *
> > > - * Both oldOptions and the result are text arrays (or NULL for
> > > "default"),
> > > - * but we declare them as Datums to avoid including array.h in
> > > reloptions.h. - */
> > > -Datum
> > > -transformRelOptions(Datum oldOptions, List *defList, const char
> > > *namspace,
> > > -                    char *validnsps[], bool 
> acceptOidsOff, 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, TYPALIGN_INT,
> > > -                          &oldoptions, NULL, 
> &noldoptions);
> > > -
> > > -        for (i = 0; i < noldoptions; i++)
> > > -        {
> > > -            char       *text_str = VARDATA(oldoptions[i]);
> > > -            int            text_len = 
> VARSIZE(oldoptions[i]) - VARHDRSZ;
> > > -
> > > -            /* Search for a match in defList */
> > > -            foreach(cell, defList)
> > > -            {
> > > -                DefElem    *def = (DefElem *) lfirst(cell);
> > > -                int            kw_len;
> > > -
> > > -                /* ignore if not in the same namespace */
> > > -                if (namspace == NULL)
> > > -                {
> > > -                    if (def->defnamespace != NULL)
> > > -                        continue;
> > > -                }
> > > -                else if (def->defnamespace == NULL)
> > > -                    continue;
> > > -                else if (strcmp(def->defnamespace, namspace) 
> != 0)
> > > -                    continue;
> > > -
> > > -                kw_len = strlen(def->defname);
> > > -                if (text_len > kw_len && text_str[kw_len] == 
> '=' &&
> > > -                    strncmp(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 (strcmp(def-
> >defnamespace, validnsps[i]) == 0)
> > > -                        {
> > > -                            valid = true;
> > > -                            break;
> > > -                        }
> > > -                    }
> > > -                }
> > > -
> > > -                if (!valid)
> > > -                    ereport(ERROR,
> > > -                            
> (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > > -                             
> errmsg("unrecognized parameter namespace \"%s\"",
> > > -                                    def-
> >defnamespace)));
> > > -            }
> > > -
> > > -            /* ignore if not in the same namespace */
> > > -            if (namspace == NULL)
> > > -            {
> > > -                if (def->defnamespace != NULL)
> > > -                    continue;
> > > -            }
> > > -            else if (def->defnamespace == NULL)
> > > -                continue;
> > > -            else if (strcmp(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";
> > > -
> > > -            /*
> > > -             * This is not a great place for this test, but 
> there's no other
> > > -             * convenient place to filter the option out. As WITH 
> (oids =
> > > -             * false) will be removed someday, this seems like 
> an acceptable
> > > -             * amount of ugly.
> > > -             */
> > > -            if (acceptOidsOff && def->defnamespace == NULL &&
> > > -                strcmp(def->defname, "oids") == 0)
> > > -            {
> > > -                if (defGetBoolean(def))
> > > -                    ereport(ERROR,
> > > -                            
> (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
> > > -                             errmsg("tables 
> declared WITH OIDS are not supported")));
> > > -                /* skip over option, reloptions machinery 
> doesn't know it */
> > > -                continue;
> > > -            }
> > > -
> > > -            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, TYPALIGN_INT,
> > > -                      &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;
> > > +    optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, 0,
> > > offset,
> > > +                                        
>     default_val, validator);
> > > +/* FIXME solve mistery with filler option! */
> > > 
> > >  }
> > >  
> > >  /*
> > > 
> > > @@ -1372,12 +245,13 @@ untransformRelOptions(Datum options)
> > > 
> > >   */
> > >  
> > >  bytea *
> > >  extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
> > > 
> > > -                  amoptions_function amoptions)
> > > +                  amreloptspecset_function 
> amoptionsspecsetfn)
> > > 
> > >  {
> > >  
> > >      bytea       *options;
> > >      bool        isnull;
> > >      Datum        datum;
> > >      Form_pg_class classForm;
> > > 
> > > +    options_spec_set *spec_set;
> > > 
> > >      datum = fastgetattr(tuple,
> > >      
> > >                          Anum_pg_class_reloptions,
> > > 
> > > @@ -1394,702 +268,341 @@ extractRelOptions(HeapTuple tuple, TupleDesc
> > > tupdesc,> 
> > >          case RELKIND_RELATION:
> > >          case RELKIND_TOASTVALUE:
> > > 
> > >          case RELKIND_MATVIEW:
> > > -            options = heap_reloptions(classForm->relkind, datum, 
> false);
> > > +            spec_set = get_heap_relopt_spec_set();
> > > 
> > >              break;
> > >          
> > >          case RELKIND_PARTITIONED_TABLE:
> > > -            options = partitioned_table_reloptions(datum, 
> false);
> > > +            spec_set = get_partitioned_relopt_spec_set();
> > > 
> > >              break;
> > >          
> > >          case RELKIND_VIEW:
> > > -            options = view_reloptions(datum, false);
> > > +            spec_set = get_view_relopt_spec_set();
> > > 
> > >              break;
> > >          
> > >          case RELKIND_INDEX:
> > > 
> > >          case RELKIND_PARTITIONED_INDEX:
> > > -            options = index_reloptions(amoptions, datum, false);
> > > +            if (amoptionsspecsetfn)
> > > +                spec_set = amoptionsspecsetfn();
> > > +            else
> > > +                spec_set = NULL;
> > > 
> > >              break;
> > >          
> > >          case RELKIND_FOREIGN_TABLE:
> > > -            options = NULL;
> > > +            spec_set = NULL;
> > > 
> > >              break;
> > >
> > >          default:
> > >              Assert(false);        /* can't get here */
> > > 
> > > -            options = NULL;        /* keep compiler quiet */
> > > +            spec_set = NULL;        /* keep compiler quiet */
> > > 
> > >              break;
> > >      
> > >      }
> > > 
> > > +    if (spec_set)
> > > +        options = optionsTextArrayToBytea(spec_set, datum, 0);
> > > +    else
> > > +        options = NULL;
> > > 
> > >      return options;
> > >  
> > >  }
> > > 
> > > -static void
> > > -parseRelOptionsInternal(Datum options, bool validate,
> > > -                        relopt_value *reloptions, 
> int numoptions)
> > > -{
> > > -    ArrayType  *array = DatumGetArrayTypeP(options);
> > > -    Datum       *optiondatums;
> > > -    int            noptions;
> > > -    int            i;
> > > -
> > > -    deconstruct_array(array, TEXTOID, -1, false, TYPALIGN_INT,
> > > -                      &optiondatums, NULL, &noptions);
> > > +options_spec_set *
> > > +get_stdrd_relopt_spec_set(relopt_kind kind)
> > > +{
> > > +    bool is_for_toast = (kind == RELOPT_KIND_TOAST);
> > > +
> > > +    options_spec_set * stdrd_relopt_spec_set = allocateOptionsSpecSet(
> > > +                    is_for_toast ? "toast" : NULL,  
> sizeof(StdRdOptions), 0); //FIXME
> > > change 0 to actual value (may be)
> > > +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "fillfactor",
> > > +                                 "Packs table 
> pages only to this percentag",
> > > +                                 
> ShareUpdateExclusiveLock,        /* since it applies only
> > > +                                        
>                          * to later inserts */
> > > +                                is_for_toast 
> ? OPTION_DEFINITION_FLAG_REJECT : 0,
> > > +                                
> offsetof(StdRdOptions, fillfactor),
> > > +                          HEAP_DEFAULT_FILLFACTOR, 
> HEAP_MIN_FILLFACTOR, 100);
> > > +    optionsSpecSetAddBool(stdrd_relopt_spec_set, "autovacuum_enabled",
> > > +                              "Enables autovacuum 
> in this relation",
> > > +                              
> ShareUpdateExclusiveLock, 0,
> > > +            offsetof(StdRdOptions, autovacuum) + 
> offsetof(AutoVacOpts, enabled),
> > > +                              true);
> > > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
> > > "autovacuum_vacuum_threshold", +                "Minimum number 
> of tuple updates or
> > > deletes prior to vacuum", +                        
>      ShareUpdateExclusiveLock,
> > > +                    0, offsetof(StdRdOptions, autovacuum) 
> + offsetof(AutoVacOpts,
> > > vacuum_threshold), +                             
> -1, 0, INT_MAX);
> > > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
> > > "autovacuum_analyze_threshold", +                "Minimum number 
> of tuple updates or
> > > deletes prior to vacuum", +                        
>      ShareUpdateExclusiveLock,
> > > +                             is_for_toast ? 
> OPTION_DEFINITION_FLAG_REJECT : 0,
> > > +                      offsetof(StdRdOptions, autovacuum) + 
> offsetof(AutoVacOpts,
> > > analyze_threshold), +                             
> -1, 0, INT_MAX);
> > > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
> > > "autovacuum_vacuum_cost_limit", +               "Vacuum cost amount 
> available
> > > before napping, for autovacuum", +                        
>      ShareUpdateExclusiveLock,
> > > +                   0, offsetof(StdRdOptions, autovacuum) + 
> offsetof(AutoVacOpts,
> > > vacuum_cost_limit), +                             
> -1, 0, 10000);
> > > +    optionsSpecSetAddInt(stdrd_relopt_spec_set, 
> "autovacuum_freeze_min_age",
> > > +     "Minimum age at which VACUUM should freeze a table row, for
> > > autovacuum",
> > > +                             
> ShareUpdateExclusiveLock,
> > > +                      0, offsetof(StdRdOptions, 
> autovacuum) + offsetof(AutoVacOpts,
> > > freeze_min_age), +                             
> -1, 0, 1000000000);
> > > +    optionsSpecSetAddInt(stdrd_relopt_spec_set, 
> "autovacuum_freeze_max_age",
> > > +    "Age at which to autovacuum a table to prevent transaction ID
> > > wraparound", +                             
> ShareUpdateExclusiveLock,
> > > +                      0, offsetof(StdRdOptions, 
> autovacuum) + offsetof(AutoVacOpts,
> > > freeze_max_age), +                             
> -1, 100000, 2000000000);
> > > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
> > > "autovacuum_freeze_table_age", +                            
>  "Age at which VACUUM should
> > > perform a full table sweep to freeze row versions", +                
>             
> > > ShareUpdateExclusiveLock,
> > > +                    0, offsetof(StdRdOptions, autovacuum) 
> + offsetof(AutoVacOpts,
> > > freeze_table_age), +                             
> -1, 0, 2000000000);
> > > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
> > > "autovacuum_multixact_freeze_min_age", +                    
>          "Minimum multixact age at
> > > which VACUUM should freeze a row multixact's, for autovacuum", +        
>                     
> > > ShareUpdateExclusiveLock,
> > > +            0, offsetof(StdRdOptions, autovacuum) + 
> offsetof(AutoVacOpts,
> > > multixact_freeze_min_age), +                            
>  -1, 0, 1000000000);
> > > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
> > > "autovacuum_multixact_freeze_max_age", +                        
>      "Multixact age at which
> > > to autovacuum a table to prevent multixact wraparound", +            
>                 
> > > ShareUpdateExclusiveLock,
> > > +            0, offsetof(StdRdOptions, autovacuum) + 
> offsetof(AutoVacOpts,
> > > multixact_freeze_max_age), +                            
>  -1, 10000, 2000000000);
> > > +    optionsSpecSetAddInt(stdrd_relopt_spec_set,
> > > "autovacuum_multixact_freeze_table_age", +                    
>          "Age of multixact at
> > > which VACUUM should perform a full table sweep to freeze row versions",
> > > +                             
> ShareUpdateExclusiveLock,
> > > +          0, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > > multixact_freeze_table_age), +                        
>      -1, 0, 2000000000);
> > > +    
> optionsSpecSetAddInt(stdrd_relopt_spec_set,"log_autovacuum_min_duration"
> > > ,
> > > +                             "Sets the minimum 
> execution time above which autovacuum actions
> > > will be logged", +                             
> ShareUpdateExclusiveLock,
> > > +                    0, offsetof(StdRdOptions, autovacuum) 
> + offsetof(AutoVacOpts,
> > > log_min_duration), +                             
> -1, -1, INT_MAX);
> > > +    optionsSpecSetAddReal(stdrd_relopt_spec_set,
> > > "autovacuum_vacuum_cost_delay", +                        
>  "Vacuum cost delay in
> > > milliseconds, for autovacuum",
> > > +                             
> ShareUpdateExclusiveLock,
> > > +                   0, offsetof(StdRdOptions, autovacuum) + 
> offsetof(AutoVacOpts,
> > > vacuum_cost_delay), +                             
> -1, 0.0, 100.0);
> > > +    optionsSpecSetAddReal(stdrd_relopt_spec_set,
> > > "autovacuum_vacuum_scale_factor", +                        
>       "Number of tuple updates or
> > > deletes prior to vacuum as a fraction of reltuples", +                
>              
> > > ShareUpdateExclusiveLock,
> > > +                 0, offsetof(StdRdOptions, autovacuum) + 
> offsetof(AutoVacOpts,
> > > vacuum_scale_factor), +                              
> -1, 0.0, 100.0);
> > > +
> > > +    optionsSpecSetAddReal(stdrd_relopt_spec_set,
> > > "autovacuum_vacuum_insert_scale_factor", +                    
>           "Number of tuple
> > > inserts prior to vacuum as a fraction of reltuples", +                
>              
> > > ShareUpdateExclusiveLock,
> > > +                 0, offsetof(StdRdOptions, autovacuum) + 
> offsetof(AutoVacOpts,
> > > vacuum_ins_scale_factor), +                            
>   -1, 0.0, 100.0);
> > > +
> > > +    optionsSpecSetAddReal(stdrd_relopt_spec_set,
> > > "autovacuum_analyze_scale_factor", +                    
>           "Number of tuple inserts,
> > > updates or deletes prior to analyze as a fraction of reltuples", +        
>                     
> > >  ShareUpdateExclusiveLock,
> > > +                              is_for_toast ? 
> OPTION_DEFINITION_FLAG_REJECT : 0,
> > > +                   offsetof(StdRdOptions, autovacuum) + 
> offsetof(AutoVacOpts,
> > > analyze_scale_factor), +                            
>   -1, 0.0, 100.0);
> > > +
> > > +
> > > +
> > > +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "toast_tuple_target",
> > > +                                 "Sets the 
> target tuple length at which external columns will be
> > > toasted", +                                
> ShareUpdateExclusiveLock,
> > > +                                is_for_toast 
> ? OPTION_DEFINITION_FLAG_REJECT : 0,
> > > +                                
> offsetof(StdRdOptions, toast_tuple_target),
> > > +                          TOAST_TUPLE_TARGET, 128, 
> TOAST_TUPLE_TARGET_MAIN);
> > > +
> > > +    optionsSpecSetAddBool(stdrd_relopt_spec_set, "user_catalog_table",
> > > +                                  "Declare a 
> table as an additional catalog table, e.g. for the
> > > purpose of logical replication", +                        
>           AccessExclusiveLock,
> > > +                                is_for_toast 
> ? OPTION_DEFINITION_FLAG_REJECT : 0,
> > > +                                 
> offsetof(StdRdOptions, user_catalog_table),
> > > +                                  false);
> > > +
> > > +    optionsSpecSetAddInt(stdrd_relopt_spec_set, "parallel_workers",
> > > +                                "Number of 
> parallel processes that can be used per executor node
> > > for this relation.", +                            
>     ShareUpdateExclusiveLock,
> > > +                                is_for_toast 
> ? OPTION_DEFINITION_FLAG_REJECT : 0,
> > > +                                
> offsetof(StdRdOptions, parallel_workers),
> > > +                                -1, 0, 1024);
> > > +
> > > +    optionsSpecSetAddEnum(stdrd_relopt_spec_set, "vacuum_index_cleanup",
> > > +                                "Controls 
> index vacuuming and index cleanup",
> > > +                                
> ShareUpdateExclusiveLock, 0,
> > > +                                
> offsetof(StdRdOptions, vacuum_index_cleanup),
> > > +                                
> StdRdOptIndexCleanupValues,
> > > +                                
> STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO,
> > > +                                
> gettext_noop("Valid values are \"on\", \"off\", and \"auto\"."));
> > > +
> > > +    optionsSpecSetAddBool(stdrd_relopt_spec_set, "vacuum_truncate",
> > > +                                "Enables 
> vacuum to truncate empty pages at the end of this
> > > table",
> > > +                                
> ShareUpdateExclusiveLock, 0,
> > > +                                
> offsetof(StdRdOptions, vacuum_truncate),
> > > +                                true);
> > > +
> > > +// FIXME Do something with OIDS
> > > +
> > > +    return stdrd_relopt_spec_set;
> > > +}
> > > +
> > > +
> > > +static options_spec_set *heap_relopt_spec_set = NULL;
> > > +
> > > +options_spec_set *
> > > +get_heap_relopt_spec_set(void)
> > > +{
> > > +    if (heap_relopt_spec_set)
> > > +        return heap_relopt_spec_set;
> > > +    heap_relopt_spec_set = get_stdrd_relopt_spec_set(RELOPT_KIND_HEAP);
> > > +    return heap_relopt_spec_set;
> > > +}
> > > +
> > > +static options_spec_set *toast_relopt_spec_set = NULL;
> > > +
> > > +options_spec_set *
> > > +get_toast_relopt_spec_set(void)
> > > +{
> > > +    if (toast_relopt_spec_set)
> > > +        return toast_relopt_spec_set;
> > > +    toast_relopt_spec_set = get_stdrd_relopt_spec_set(RELOPT_KIND_TOAST);
> > > +    return toast_relopt_spec_set;
> > > +}
> > > +
> > > +static options_spec_set *partitioned_relopt_spec_set = NULL;
> > > 
> > > -    for (i = 0; i < noptions; i++)
> > > -    {
> > > -        char       *text_str = VARDATA(optiondatums[i]);
> > > -        int            text_len = VARSIZE(optiondatums[i]) 
> - VARHDRSZ;
> > > -        int            j;
> > > -
> > > -        /* Search for a match in reloptions */
> > > -        for (j = 0; j < numoptions; j++)
> > > -        {
> > > -            int            kw_len = reloptions[j].gen-
> >namelen;
> > > -
> > > -            if (text_len > kw_len && text_str[kw_len] == '=' &&
> > > -                strncmp(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);
> > > +options_spec_set *
> > > +get_partitioned_relopt_spec_set(void)
> > > +{
> > > +    if (partitioned_relopt_spec_set)
> > > +        return partitioned_relopt_spec_set;
> > > +    partitioned_relopt_spec_set = allocateOptionsSpecSet(
> > > +                    NULL,  sizeof(StdRdOptions), 0);
> > > +    /* No options for now, so spec set is empty */
> > > 
> > > -    if (((void *) array) != DatumGetPointer(options))
> > > -        pfree(array);
> > > +    return partitioned_relopt_spec_set;
> > > 
> > >  }
> > >  
> > >  /*
> > > 
> > > - * Interpret reloptions that are given in text-array format.
> > > - *
> > > - * options is a reloption text array as constructed by
> > > transformRelOptions. - * kind specifies the family of options to be
> > > processed.
> > > - *
> > > - * The return value is a relopt_value * array on which the options
> > > actually - * set in the options array are marked with isset=true.  The
> > > length of this - * array is returned in *numrelopts.  Options not set are
> > > also present in the - * array; this is so that the caller can easily
> > > locate the default values. - *
> > > - * If there are no options of the given kind, numrelopts is set to 0 and
> > > NULL - * is returned (unless options are illegally supplied despite none
> > > being - * defined, in which case an error occurs).
> > > - *
> > > - * Note: values of type int, bool and real are allocated as part of the
> > > - * returned array.  Values of type string are allocated separately and
> > > must - * be freed by the caller.
> > > + * Parse local options, allocate a bytea struct that's of the specified
> > > + * 'base_size' plus any extra space that's needed for string variables,
> > > + * fill its option's fields located at the given offsets and return it.
> > > 
> > >   */
> > > 
> > > -static relopt_value *
> > > -parseRelOptions(Datum options, bool validate, relopt_kind kind,
> > > -                int *numrelopts)
> > > -{
> > > -    relopt_value *reloptions = NULL;
> > > -    int            numoptions = 0;
> > > -    int            i;
> > > -    int            j;
> > > -
> > > -    if (need_initialization)
> > > -        initialize_reloptions();
> > > -
> > > -    /* Build a list of expected options, based on kind */
> > > -
> > > -    for (i = 0; relOpts[i]; i++)
> > > -        if (relOpts[i]->kinds & kind)
> > > -            numoptions++;
> > > -
> > > -    if (numoptions > 0)
> > > -    {
> > > -        reloptions = palloc(numoptions * sizeof(relopt_value));
> > > -
> > > -        for (i = 0, j = 0; relOpts[i]; i++)
> > > -        {
> > > -            if (relOpts[i]->kinds & kind)
> > > -            {
> > > -                reloptions[j].gen = relOpts[i];
> > > -                reloptions[j].isset = false;
> > > -                j++;
> > > -            }
> > > -        }
> > > -    }
> > > -
> > > -    /* Done if no options */
> > > -    if (PointerIsValid(DatumGetPointer(options)))
> > > -        parseRelOptionsInternal(options, validate, reloptions, 
> numoptions);
> > > -
> > > -    *numrelopts = numoptions;
> > > -    return reloptions;
> > > -}
> > > -
> > > -/* Parse local unregistered options. */
> > > -static relopt_value *
> > > -parseLocalRelOptions(local_relopts *relopts, Datum options, bool
> > > validate)
> > > +void *
> > > +build_local_reloptions(local_relopts *relopts, Datum options, bool
> > > validate)> 
> > >  {
> > > 
> > > -    int            nopts = list_length(relopts->options);
> > > -    relopt_value *values = palloc(sizeof(*values) * nopts);
> > > +    void       *opts;
> > > 
> > >      ListCell   *lc;
> > > 
> > > -    int            i = 0;
> > > -
> > > -    foreach(lc, relopts->options)
> > > -    {
> > > -        local_relopt *opt = lfirst(lc);
> > > -
> > > -        values[i].gen = opt->option;
> > > -        values[i].isset = false;
> > > -
> > > -        i++;
> > > -    }
> > > -
> > > -    if (options != (Datum) 0)
> > > -        parseRelOptionsInternal(options, validate, values, nopts);
> > > +    opts = (void *) optionsTextArrayToBytea(relopts->spec_set, options,
> > > validate);
> > > 
> > > -    return values;
> > > -}
> > > -
> > > -/*
> > > - * Subroutine for parseRelOptions, to parse and validate a single
> > > option's
> > > - * value
> > > - */
> > > -static void
> > > -parse_one_reloption(relopt_value *option, char *text_str, int text_len,
> > > -                    bool validate)
> > > -{
> > > -    char       *value;
> > > -    int            value_len;
> > > -    bool        parsed;
> > > -    bool        nofree = false;
> > > -
> > > -    if (option->isset && validate)
> > > -        ereport(ERROR,
> > > -                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > > -                 errmsg("parameter \"%s\" specified more than 
> once",
> > > -                        option->gen->name)));
> > > -
> > > -    value_len = text_len - option->gen->namelen - 1;
> > > -    value = (char *) palloc(value_len + 1);
> > > -    memcpy(value, text_str + option->gen->namelen + 1, value_len);
> > > -    value[value_len] = '\0';
> > > -
> > > -    switch (option->gen->type)
> > > -    {
> > > -        case RELOPT_TYPE_BOOL:
> > > -            {
> > > -                parsed = parse_bool(value, &option-
> >values.bool_val);
> > > -                if (validate && !parsed)
> > > -                    ereport(ERROR,
> > > -                            
> (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > > -                             errmsg("invalid 
> value for boolean option \"%s\": %s",
> > > -                                    
> option->gen->name, value)));
> > > -            }
> > > -            break;
> > > -        case RELOPT_TYPE_INT:
> > > -            {
> > > -                relopt_int *optint = (relopt_int *) option-
> >gen;
> > > -
> > > -                parsed = parse_int(value, &option-
> >values.int_val, 0, NULL);
> > > -                if (validate && !parsed)
> > > -                    ereport(ERROR,
> > > -                            
> (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > > -                             errmsg("invalid 
> value for integer option \"%s\": %s",
> > > -                                    
> option->gen->name, value)));
> > > -                if (validate && (option->values.int_val < 
> optint->min ||
> > > -                                 option-
> >values.int_val > optint->max))
> > > -                    ereport(ERROR,
> > > -                            
> (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > > -                             errmsg("value %s 
> out of bounds for option \"%s\"",
> > > -                                    
> value, option->gen->name),
> > > -                             errdetail("Valid 
> values are between \"%d\" and \"%d\".",
> > > -                                       
> optint->min, optint->max)));
> > > -            }
> > > -            break;
> > > -        case RELOPT_TYPE_REAL:
> > > -            {
> > > -                relopt_real *optreal = (relopt_real *) 
> option->gen;
> > > -
> > > -                parsed = parse_real(value, &option-
> >values.real_val, 0, NULL);
> > > -                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_ENUM:
> > > -            {
> > > -                relopt_enum *optenum = (relopt_enum *) 
> option->gen;
> > > -                relopt_enum_elt_def *elt;
> > > -
> > > -                parsed = false;
> > > -                for (elt = optenum->members; elt-
> >string_val; elt++)
> > > -                {
> > > -                    if (pg_strcasecmp(value, elt-
> >string_val) == 0)
> > > -                    {
> > > -                        option->values.enum_val = 
> elt->symbol_val;
> > > -                        parsed = true;
> > > -                        break;
> > > -                    }
> > > -                }
> > > -                if (validate && !parsed)
> > > -                    ereport(ERROR,
> > > -                            
> (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> > > -                             errmsg("invalid 
> value for enum option \"%s\": %s",
> > > -                                    
> option->gen->name, value),
> > > -                             optenum->detailmsg 
> ?
> > > -                             
> errdetail_internal("%s", _(optenum->detailmsg)) : 0));
> > > -
> > > -                /*
> > > -                 * If value is not among the allowed string 
> values, but we are
> > > -                 * not asked to validate, just use the 
> default numeric value.
> > > -                 */
> > > -                if (!parsed)
> > > -                    option->values.enum_val = optenum-
> >default_val;
> > > -            }
> > > -            break;
> > > -        case RELOPT_TYPE_STRING:
> > > -            {
> > > -                relopt_string *optstring = (relopt_string *) 
> option->gen;
> > > -
> > > -                option->values.string_val = value;
> > > -                nofree = true;
> > > -                if (validate && optstring->validate_cb)
> > > -                    (optstring->validate_cb) (value);
> > > -                parsed = true;
> > > -            }
> > > -            break;
> > > -        default:
> > > -            elog(ERROR, "unsupported reloption type %d", option-
> >gen->type);
> > > -            parsed = true;        /* quiet compiler */
> > > -            break;
> > > -    }
> > > +    foreach(lc, relopts->validators)
> > > +        ((relopts_validator) lfirst(lc)) (opts, NULL, 0);
> > > +//        ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
> > > +// FIXME solve problem with validation of separate option values;
> > > +    return opts;
> > > 
> > > -    if (parsed)
> > > -        option->isset = true;
> > > -    if (!nofree)
> > > -        pfree(value);
> > > 
> > >  }
> > >  
> > >  /*
> > > 
> > > - * Given the result from parseRelOptions, allocate a struct that's of the
> > > - * specified base size plus any extra space that's needed for string
> > > variables. - *
> > > - * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions
> > > or - * equivalent).
> > > + * get_view_relopt_spec_set
> > > + *        Returns an options catalog for view relation.
> > > 
> > >   */
> > > 
> > > -static void *
> > > -allocateReloptStruct(Size base, relopt_value *options, int numoptions)
> > > -{
> > > -    Size        size = base;
> > > -    int            i;
> > > -
> > > -    for (i = 0; i < numoptions; i++)
> > > -    {
> > > -        relopt_value *optval = &options[i];
> > > -
> > > -        if (optval->gen->type == RELOPT_TYPE_STRING)
> > > -        {
> > > -            relopt_string *optstr = (relopt_string *) optval-
> >gen;
> > > -
> > > -            if (optstr->fill_cb)
> > > -            {
> > > -                const char *val = optval->isset ? optval-
> >values.string_val :
> > > -                optstr->default_isnull ? NULL : optstr-
> >default_val;
> > > -
> > > -                size += optstr->fill_cb(val, NULL);
> > > -            }
> > > -            else
> > > -                size += GET_STRING_RELOPTION_LEN(*optval) + 
> 1;
> > > -        }
> > > -    }
> > > -
> > > -    return palloc0(size);
> > > -}
> > > +static options_spec_set *view_relopt_spec_set = NULL;
> > > 
> > > -/*
> > > - * 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.
> > > - */
> > > -static void
> > > -fillRelOptions(void *rdopts, Size basesize,
> > > -               relopt_value *options, int numoptions,
> > > -               bool validate,
> > > -               const relopt_parse_elt *elems, int numelems)
> > > +options_spec_set *
> > > +get_view_relopt_spec_set(void)
> > > 
> > >  {
> > > 
> > > -    int            i;
> > > -    int            offset = basesize;
> > > +    if (view_relopt_spec_set)
> > > +        return view_relopt_spec_set;
> > > 
> > > -    for (i = 0; i < numoptions; i++)
> > > -    {
> > > -        int            j;
> > > -        bool        found = false;
> > > +    view_relopt_spec_set = allocateOptionsSpecSet(NULL,
> > > +                                        
>          sizeof(ViewOptions), 2);
> > > 
> > > -        for (j = 0; j < numelems; j++)
> > > -        {
> > > -            if (strcmp(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_ENUM:
> > > -                        *(int *) itempos = 
> options[i].isset ?
> > > -                            
> options[i].values.enum_val :
> > > -                            ((relopt_enum *) 
> options[i].gen)->default_val;
> > > -                        break;
> > > -                    case RELOPT_TYPE_STRING:
> > > -                        optstring = (relopt_string
> *) options[i].gen;
> > > -                        if (options[i].isset)
> > > -                            string_val = 
> options[i].values.string_val;
> > > -                        else if (!optstring-
> >default_isnull)
> > > -                            string_val = 
> optstring->default_val;
> > > -                        else
> > > -                            string_val = NULL;
> > > -
> > > -                        if (optstring->fill_cb)
> > > -                        {
> > > -                            Size        
> size =
> > > -                            optstring-
> >fill_cb(string_val,
> > > -                                        
>        (char *) rdopts + offset);
> > > -
> > > -                            if (size)
> > > -                            {
> > > -                                *(int *) 
> itempos = offset;
> > > -                                offset += 
> size;
> > > -                            }
> > > -                            else
> > > -                                *(int *) 
> itempos = 0;
> > > -                        }
> > > -                        else if (string_val == NULL)
> > > -                            *(int *) itempos = 
> 0;
> > > -                        else
> > > -                        {
> > > -                            strcpy((char *) 
> rdopts + offset, string_val);
> > > -                            *(int *) itempos = 
> offset;
> > > -                            offset += 
> strlen(string_val) + 1;
> > > -                        }
> > > -                        break;
> > > -                    default:
> > > -                        elog(ERROR, "unsupported 
> reloption type %d",
> > > -                             options[i].gen-
> >type);
> > > -                        break;
> > > -                }
> > > -                found = true;
> > > -                break;
> > > -            }
> > > -        }
> > > -        if (validate && !found)
> > > -            elog(ERROR, "reloption \"%s\" not found in parse 
> table",
> > > -                 options[i].gen->name);
> > > -    }
> > > -    SET_VARSIZE(rdopts, offset);
> > > -}
> > > +    optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
> > > +                              "View acts as a row 
> security barrier",
> > > +                              
> AccessExclusiveLock,
> > > +                      0, offsetof(ViewOptions, 
> security_barrier), false);
> > > 
> > > +    optionsSpecSetAddEnum(view_relopt_spec_set, "check_option",
> > > +                           "View has WITH CHECK 
> OPTION defined (local or cascaded)",
> > > +                              
> AccessExclusiveLock, 0,
> > > +                              
> offsetof(ViewOptions, check_option),
> > > +                              viewCheckOptValues,
> > > +                              
> VIEW_OPTION_CHECK_OPTION_NOT_SET,
> > > +                              gettext_noop("Valid 
> values are \"local\" and \"cascaded\"."));
> > > 
> > > -/*
> > > - * Option parser for anything that uses StdRdOptions.
> > > - */
> > > -bytea *
> > > -default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
> > > -{
> > > -    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_vacuum_insert_threshold",
> > > RELOPT_TYPE_INT,
> > > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > > vacuum_ins_threshold)}, -        {"autovacuum_analyze_threshold",
> > > RELOPT_TYPE_INT,
> > > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > > analyze_threshold)}, -        {"autovacuum_vacuum_cost_limit", 
> RELOPT_TYPE_INT,
> > > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > > vacuum_cost_limit)}, -        {"autovacuum_freeze_min_age", 
> RELOPT_TYPE_INT,
> > > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > > freeze_min_age)}, -        {"autovacuum_freeze_max_age", 
> RELOPT_TYPE_INT,
> > > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > > freeze_max_age)}, -        {"autovacuum_freeze_table_age", 
> RELOPT_TYPE_INT,
> > > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > > freeze_table_age)}, -        {"autovacuum_multixact_freeze_min_age",
> > > RELOPT_TYPE_INT,
> > > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > > multixact_freeze_min_age)}, -        
> {"autovacuum_multixact_freeze_max_age",
> > > RELOPT_TYPE_INT,
> > > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > > multixact_freeze_max_age)}, -        
> {"autovacuum_multixact_freeze_table_age",
> > > RELOPT_TYPE_INT,
> > > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > > multixact_freeze_table_age)}, -        
> {"log_autovacuum_min_duration",
> > > RELOPT_TYPE_INT,
> > > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > > log_min_duration)}, -        {"toast_tuple_target", RELOPT_TYPE_INT,
> > > -        offsetof(StdRdOptions, toast_tuple_target)},
> > > -        {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_REAL,
> > > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > > vacuum_cost_delay)}, -        {"autovacuum_vacuum_scale_factor",
> > > RELOPT_TYPE_REAL,
> > > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > > vacuum_scale_factor)}, -        
> {"autovacuum_vacuum_insert_scale_factor",
> > > RELOPT_TYPE_REAL,
> > > -        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts,
> > > vacuum_ins_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)},
> > > -        {"vacuum_index_cleanup", RELOPT_TYPE_ENUM,
> > > -        offsetof(StdRdOptions, vacuum_index_cleanup)},
> > > -        {"vacuum_truncate", RELOPT_TYPE_BOOL,
> > > -        offsetof(StdRdOptions, vacuum_truncate)}
> > > -    };
> > > -
> > > -    return (bytea *) build_reloptions(reloptions, validate, kind,
> > > -                                      
> sizeof(StdRdOptions),
> > > -                                      
> tab, lengthof(tab));
> > > +    return view_relopt_spec_set;
> > > 
> > >  }
> > >  
> > >  /*
> > > 
> > > - * build_reloptions
> > > - *
> > > - * Parses "reloptions" provided by the caller, returning them in a
> > > - * structure containing the parsed options.  The parsing is done with
> > > - * the help of a parsing table describing the allowed options, defined
> > > - * by "relopt_elems" of length "num_relopt_elems".
> > > - *
> > > - * "validate" must be true if reloptions value is freshly built by
> > > - * transformRelOptions(), as opposed to being read from the catalog, in
> > > which - * case the values contained in it must already be valid.
> > > - *
> > > - * NULL is returned if the passed-in options did not match any of the
> > > options - * in the parsing table, unless validate is true in which case
> > > an error would - * be reported.
> > > + * get_attribute_options_spec_set
> > > + *        Returns an options spec det for heap attributes
> > > 
> > >   */
> > > 
> > > -void *
> > > -build_reloptions(Datum reloptions, bool validate,
> > > -                 relopt_kind kind,
> > > -                 Size relopt_struct_size,
> > > -                 const relopt_parse_elt *relopt_elems,
> > > -                 int num_relopt_elems)
> > > -{
> > > -    int            numoptions;
> > > -    relopt_value *options;
> > > -    void       *rdopts;
> > > -
> > > -    /* parse options specific to given relation option kind */
> > > -    options = parseRelOptions(reloptions, validate, kind, &numoptions);
> > > -    Assert(numoptions <= num_relopt_elems);
> > > -
> > > -    /* if none set, we're done */
> > > -    if (numoptions == 0)
> > > -    {
> > > -        Assert(options == NULL);
> > > -        return NULL;
> > > -    }
> > > -
> > > -    /* allocate and fill the structure */
> > > -    rdopts = allocateReloptStruct(relopt_struct_size, options, 
> numoptions);
> > > -    fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
> > > -                   validate, relopt_elems, 
> num_relopt_elems);
> > > +static options_spec_set *attribute_options_spec_set = NULL;
> > > 
> > > -    pfree(options);
> > > -
> > > -    return rdopts;
> > > -}
> > > -
> > > -/*
> > > - * Parse local options, allocate a bytea struct that's of the specified
> > > - * 'base_size' plus any extra space that's needed for string variables,
> > > - * fill its option's fields located at the given offsets and return it.
> > > - */
> > > -void *
> > > -build_local_reloptions(local_relopts *relopts, Datum options, bool
> > > validate) +options_spec_set *
> > > +get_attribute_options_spec_set(void)
> > > 
> > >  {
> > > 
> > > -    int            noptions = list_length(relopts->options);
> > > -    relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
> > > -    relopt_value *vals;
> > > -    void       *opts;
> > > -    int            i = 0;
> > > -    ListCell   *lc;
> > > +    if (attribute_options_spec_set)
> > > +            return attribute_options_spec_set;
> > > 
> > > -    foreach(lc, relopts->options)
> > > -    {
> > > -        local_relopt *opt = lfirst(lc);
> > > -
> > > -        elems[i].optname = opt->option->name;
> > > -        elems[i].opttype = opt->option->type;
> > > -        elems[i].offset = opt->offset;
> > > -
> > > -        i++;
> > > -    }
> > > +    attribute_options_spec_set = allocateOptionsSpecSet(NULL,
> > > +                                        
>        sizeof(AttributeOpts), 2);
> > > 
> > > -    vals = parseLocalRelOptions(relopts, options, validate);
> > > -    opts = allocateReloptStruct(relopts->relopt_struct_size, vals,
> > > noptions);
> > > -    fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions,
> > > validate, -                   elems, noptions);
> > > +    optionsSpecSetAddReal(attribute_options_spec_set, "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);
> > > 
> > > -    foreach(lc, relopts->validators)
> > > -        ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
> > > -
> > > -    if (elems)
> > > -        pfree(elems);
> > > +    optionsSpecSetAddReal(attribute_options_spec_set,
> > > +                          "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 opts;
> > > +    return attribute_options_spec_set;
> > > 
> > >  }
> > > 
> > > -/*
> > > - * Option parser for partitioned tables
> > > - */
> > > -bytea *
> > > -partitioned_table_reloptions(Datum reloptions, bool validate)
> > > -{
> > > -    /*
> > > -     * There are no options for partitioned tables yet, but this is able 
> to
> > > do -     * some validation.
> > > -     */
> > > -    return (bytea *) build_reloptions(reloptions, validate,
> > > -                                      
> RELOPT_KIND_PARTITIONED,
> > > -                                      0, 
> NULL, 0);
> > > -}
> > > 
> > >  /*
> > > 
> > > - * Option parser for views
> > > - */
> > > -bytea *
> > > -view_reloptions(Datum reloptions, bool validate)
> > > -{
> > > -    static const relopt_parse_elt tab[] = {
> > > -        {"security_barrier", RELOPT_TYPE_BOOL,
> > > -        offsetof(ViewOptions, security_barrier)},
> > > -        {"check_option", RELOPT_TYPE_ENUM,
> > > -        offsetof(ViewOptions, check_option)}
> > > -    };
> > > -
> > > -    return (bytea *) build_reloptions(reloptions, validate,
> > > -                                      
> RELOPT_KIND_VIEW,
> > > -                                      
> sizeof(ViewOptions),
> > > -                                      
> tab, lengthof(tab));
> > > -}
> > > + * get_tablespace_options_spec_set
> > > + *        Returns an options spec set for tablespaces
> > > +*/
> > > +static options_spec_set *tablespace_options_spec_set = NULL;
> > > 
> > > -/*
> > > - * Parse options for heaps, views and toast tables.
> > > - */
> > > -bytea *
> > > -heap_reloptions(char relkind, Datum reloptions, bool validate)
> > > +options_spec_set *
> > > +get_tablespace_options_spec_set(void)
> > > 
> > >  {
> > > 
> > > -    StdRdOptions *rdopts;
> > > -
> > > -    switch (relkind)
> > > +    if (!tablespace_options_spec_set)
> > > 
> > >      {
> > > 
> > > -        case RELKIND_TOASTVALUE:
> > > -            rdopts = (StdRdOptions *)
> > > -                default_reloptions(reloptions, validate, 
> RELOPT_KIND_TOAST);
> > > -            if (rdopts != NULL)
> > > -            {
> > > -                /* adjust default-only parameters for TOAST 
> relations */
> > > -                rdopts->fillfactor = 100;
> > > -                rdopts->autovacuum.analyze_threshold = -1;
> > > -                rdopts->autovacuum.analyze_scale_factor = 
> -1;
> > > -            }
> > > -            return (bytea *) rdopts;
> > > -        case RELKIND_RELATION:
> > > -        case RELKIND_MATVIEW:
> > > -            return default_reloptions(reloptions, validate, 
> RELOPT_KIND_HEAP);
> > > -        default:
> > > -            /* other relkinds are not supported */
> > > -            return NULL;
> > > -    }
> > > -}
> > > -
> > > -
> > > -/*
> > > - * Parse options for indexes.
> > > - *
> > > - *    amoptions    index AM's option parser function
> > > - *    reloptions    options as text[] datum
> > > - *    validate    error flag
> > > - */
> > > -bytea *
> > > -index_reloptions(amoptions_function amoptions, Datum reloptions, bool
> > > validate) -{
> > > -    Assert(amoptions != NULL);
> > > +        tablespace_options_spec_set = allocateOptionsSpecSet(NULL,
> > > +                                        
>           sizeof(TableSpaceOpts), 4);
> > > 
> > > -    /* Assume function is strict */
> > > -    if (!PointerIsValid(DatumGetPointer(reloptions)))
> > > -        return NULL;
> > > +        optionsSpecSetAddReal(tablespace_options_spec_set,
> > > +                                  
> "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);
> > > 
> > > -    return amoptions(reloptions, validate);
> > > -}
> > > +        optionsSpecSetAddReal(tablespace_options_spec_set, 
> "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);
> > > 
> > > -/*
> > > - * Option parser for attribute reloptions
> > > - */
> > > -bytea *
> > > -attribute_reloptions(Datum reloptions, bool validate)
> > > -{
> > > -    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)} -    };
> > > -
> > > -    return (bytea *) build_reloptions(reloptions, validate,
> > > -                                      
> RELOPT_KIND_ATTRIBUTE,
> > > -                                      
> sizeof(AttributeOpts),
> > > -                                      
> tab, lengthof(tab));
> > > -}
> > > +        optionsSpecSetAddInt(tablespace_options_spec_set,
> > > +                                 
> "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
> > > +            );
> > > 
> > > -/*
> > > - * Option parser for tablespace reloptions
> > > - */
> > > -bytea *
> > > -tablespace_reloptions(Datum reloptions, bool validate)
> > > -{
> > > -    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)},
> > > -        {"maintenance_io_concurrency", RELOPT_TYPE_INT,
> > > offsetof(TableSpaceOpts, maintenance_io_concurrency)} -    };
> > > -
> > > -    return (bytea *) build_reloptions(reloptions, validate,
> > > -                                      
> RELOPT_KIND_TABLESPACE,
> > > -                                      
> sizeof(TableSpaceOpts),
> > > -                                      
> tab, lengthof(tab));
> > > +        optionsSpecSetAddInt(tablespace_options_spec_set,
> > > +                                 
> "maintenance_io_concurrency",
> > > +                                 "Number of 
> simultaneous requests that can be handled efficiently
> > > by the disk subsystem for maintenance work.", +                
>                 
> > > ShareUpdateExclusiveLock,
> > > +                       0, offsetof(TableSpaceOpts, 
> maintenance_io_concurrency),
> > > +#ifdef USE_PREFETCH
> > > +                                 -1, 0,
> MAX_IO_CONCURRENCY
> > > +#else
> > > +                                 0, 0, 0
> > > +#endif
> > > +            );
> > > +    }
> > > +    return tablespace_options_spec_set;
> > > 
> > >  }
> > >  
> > >  /*
> > > 
> > > @@ -2099,33 +612,55 @@ tablespace_reloptions(Datum reloptions, bool
> > > validate)> 
> > >   * for a longer explanation of how this works.
> > >   */
> > >  
> > >  LOCKMODE
> > > 
> > > -AlterTableGetRelOptionsLockLevel(List *defList)
> > > +AlterTableGetRelOptionsLockLevel(Relation rel, List *defList)
> > > 
> > >  {
> > >  
> > >      LOCKMODE    lockmode = NoLock;
> > >      ListCell   *cell;
> > > 
> > > +    options_spec_set *spec_set = NULL;
> > > 
> > >      if (defList == NIL)
> > >      
> > >          return AccessExclusiveLock;
> > > 
> > > -    if (need_initialization)
> > > -        initialize_reloptions();
> > > +    switch (rel->rd_rel->relkind)
> > > +    {
> > > +        case RELKIND_TOASTVALUE:
> > > +            spec_set = get_toast_relopt_spec_set();
> > > +            break;
> > > +        case RELKIND_RELATION:
> > > +        case RELKIND_MATVIEW:
> > > +            spec_set = get_heap_relopt_spec_set();
> > > +            break;
> > > +        case RELKIND_INDEX:
> > > +            spec_set = rel->rd_indam->amreloptspecset();
> > > +            break;
> > > +        case RELKIND_VIEW:
> > > +            spec_set = get_view_relopt_spec_set();
> > > +            break;
> > > +        case RELKIND_PARTITIONED_TABLE:
> > > +            spec_set = get_partitioned_relopt_spec_set();
> > > +            break;
> > > +        default:
> > > +            Assert(false);        /* can't get here */
> > > +            break;
> > > +    }
> > > +    Assert(spec_set);            /* No spec set - no reloption 
> change. Should
> > > +                                 * never get 
> here */
> > > 
> > >      foreach(cell, defList)
> > >      {
> > >      
> > >          DefElem    *def = (DefElem *) lfirst(cell);
> > > 
> > > +
> > > 
> > >          int            i;
> > > 
> > > -        for (i = 0; relOpts[i]; i++)
> > > +        for (i = 0; i < spec_set->num; i++)
> > > 
> > >          {
> > > 
> > > -            if (strncmp(relOpts[i]->name,
> > > -                        def->defname,
> > > -                        relOpts[i]->namelen + 1) == 
> 0)
> > > -            {
> > > -                if (lockmode < relOpts[i]->lockmode)
> > > -                    lockmode = relOpts[i]->lockmode;
> > > -            }
> > > +            option_spec_basic *gen = spec_set->definitions[i];
> > > +
> > > +            if (pg_strcasecmp(gen->name,
> > > +                              def->defname) == 0)
> > > +                if (lockmode < gen->lockmode)
> > > +                    lockmode = gen->lockmode;
> > > 
> > >          }
> > >      
> > >      }
> > > 
> > > -
> > > 
> > >      return lockmode;
> > > 
> > > -}
> > > +}
> > > \ No newline at end of file
> > > diff --git a/src/backend/access/gin/gininsert.c
> > > b/src/backend/access/gin/gininsert.c index 0e8672c..0cbffad 100644
> > > --- a/src/backend/access/gin/gininsert.c
> > > +++ b/src/backend/access/gin/gininsert.c
> > > @@ -512,6 +512,8 @@ gininsert(Relation index, Datum *values, bool *isnull,
> > > 
> > >      oldCtx = MemoryContextSwitchTo(insertCtx);
> > > 
> > > +// elog(WARNING, "GinGetUseFastUpdate = %i", GinGetUseFastUpdate(index));
> > > +
> > > 
> > >      if (GinGetUseFastUpdate(index))
> > >      {
> > >      
> > >          GinTupleCollector collector;
> > > 
> > > diff --git a/src/backend/access/gin/ginutil.c
> > > b/src/backend/access/gin/ginutil.c index 6d2d71b..d1fa3a0 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"
> > > 
> > > @@ -28,6 +28,7 @@
> > > 
> > >  #include "utils/builtins.h"
> > >  #include "utils/index_selfuncs.h"
> > >  #include "utils/typcache.h"
> > > 
> > > +#include "utils/guc.h"
> > > 
> > >  /*
> > > 
> > > @@ -67,7 +68,6 @@ ginhandler(PG_FUNCTION_ARGS)
> > > 
> > >      amroutine->amvacuumcleanup = ginvacuumcleanup;
> > >      amroutine->amcanreturn = NULL;
> > >      amroutine->amcostestimate = gincostestimate;
> > > 
> > > -    amroutine->amoptions = ginoptions;
> > > 
> > >      amroutine->amproperty = NULL;
> > >      amroutine->ambuildphasename = NULL;
> > >      amroutine->amvalidate = ginvalidate;
> > > 
> > > @@ -82,6 +82,7 @@ ginhandler(PG_FUNCTION_ARGS)
> > > 
> > >      amroutine->amestimateparallelscan = NULL;
> > >      amroutine->aminitparallelscan = NULL;
> > >      amroutine->amparallelrescan = NULL;
> > > 
> > > +    amroutine->amreloptspecset = gingetreloptspecset;
> > > 
> > >      PG_RETURN_POINTER(amroutine);
> > >  
> > >  }
> > > 
> > > @@ -604,6 +605,7 @@ ginExtractEntries(GinState *ginstate, OffsetNumber
> > > attnum,> 
> > >      return entries;
> > >  
> > >  }
> > > 
> > > +/*
> > > 
> > >  bytea *
> > >  ginoptions(Datum reloptions, bool validate)
> > >  {
> > > 
> > > @@ -618,6 +620,7 @@ ginoptions(Datum reloptions, bool validate)
> > > 
> > >                                        
> sizeof(GinOptions),
> > >                                        
> tab, lengthof(tab));
> > >  
> > >  }
> > > 
> > > +*/
> > > 
> > >  /*
> > >  
> > >   * Fetch index's statistical data into *stats
> > > 
> > > @@ -705,3 +708,31 @@ ginUpdateStats(Relation index, const GinStatsData
> > > *stats, bool is_build)> 
> > >      END_CRIT_SECTION();
> > >  
> > >  }
> > > 
> > > +
> > > +static options_spec_set *gin_relopt_specset = NULL;
> > > +
> > > +void *
> > > +gingetreloptspecset(void)
> > > +{
> > > +    if (gin_relopt_specset)
> > > +        return gin_relopt_specset;
> > > +
> > > +    gin_relopt_specset = allocateOptionsSpecSet(NULL,
> > > +                                        
>         sizeof(GinOptions), 2);
> > > +
> > > +    optionsSpecSetAddBool(gin_relopt_specset, "fastupdate",
> > > +                        "Enables \"fast update\" 
> feature for this GIN index",
> > > +                              
> AccessExclusiveLock,
> > > +                              0,
> > > +                              offsetof(GinOptions, 
> useFastUpdate),
> > > +                              
> GIN_DEFAULT_USE_FASTUPDATE);
> > > +
> > > +    optionsSpecSetAddInt(gin_relopt_specset, "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_specset;
> > > +}
> > > diff --git a/src/backend/access/gist/gist.c
> > > b/src/backend/access/gist/gist.c index 0683f42..cbbc6a5 100644
> > > --- a/src/backend/access/gist/gist.c
> > > +++ b/src/backend/access/gist/gist.c
> > > @@ -88,7 +88,6 @@ gisthandler(PG_FUNCTION_ARGS)
> > > 
> > >      amroutine->amvacuumcleanup = gistvacuumcleanup;
> > >      amroutine->amcanreturn = gistcanreturn;
> > >      amroutine->amcostestimate = gistcostestimate;
> > > 
> > > -    amroutine->amoptions = gistoptions;
> > > 
> > >      amroutine->amproperty = gistproperty;
> > >      amroutine->ambuildphasename = NULL;
> > >      amroutine->amvalidate = gistvalidate;
> > > 
> > > @@ -103,6 +102,7 @@ gisthandler(PG_FUNCTION_ARGS)
> > > 
> > >      amroutine->amestimateparallelscan = NULL;
> > >      amroutine->aminitparallelscan = NULL;
> > >      amroutine->amparallelrescan = NULL;
> > > 
> > > +    amroutine->amreloptspecset = gistgetreloptspecset;
> > > 
> > >      PG_RETURN_POINTER(amroutine);
> > >  
> > >  }
> > > 
> > > diff --git a/src/backend/access/gist/gistbuild.c
> > > b/src/backend/access/gist/gistbuild.c index baad28c..931d249 100644
> > > --- a/src/backend/access/gist/gistbuild.c
> > > +++ b/src/backend/access/gist/gistbuild.c
> > > @@ -215,6 +215,7 @@ gistbuild(Relation heap, Relation index, IndexInfo
> > > *indexInfo)> 
> > >              buildstate.buildMode = GIST_BUFFERING_DISABLED;
> > >          
> > >          else                    /* must be "auto" 
> */
> > >          
> > >              buildstate.buildMode = GIST_BUFFERING_AUTO;
> > > 
> > > +//elog(WARNING, "biffering_mode = %i", options->buffering_mode);
> > > 
> > >      }
> > >      else
> > >      {
> > > 
> > > diff --git a/src/backend/access/gist/gistutil.c
> > > b/src/backend/access/gist/gistutil.c index 43ba03b..0391915 100644
> > > --- a/src/backend/access/gist/gistutil.c
> > > +++ b/src/backend/access/gist/gistutil.c
> > > @@ -17,7 +17,7 @@
> > > 
> > >  #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"
> > > 
> > > @@ -916,20 +916,6 @@ gistPageRecyclable(Page page)
> > > 
> > >      return false;
> > >  
> > >  }
> > > 
> > > -bytea *
> > > -gistoptions(Datum reloptions, bool validate)
> > > -{
> > > -    static const relopt_parse_elt tab[] = {
> > > -        {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, 
> fillfactor)},
> > > -        {"buffering", RELOPT_TYPE_ENUM, offsetof(GiSTOptions, 
> buffering_mode)}
> > > -    };
> > > -
> > > -    return (bytea *) build_reloptions(reloptions, validate,
> > > -                                      
> RELOPT_KIND_GIST,
> > > -                                      
> sizeof(GiSTOptions),
> > > -                                      
> tab, lengthof(tab));
> > > -}
> > > -
> > > 
> > >  /*
> > >  
> > >   *    gistproperty() -- Check boolean properties of indexes.
> > >   *
> > > 
> > > @@ -1064,3 +1050,42 @@ gistGetFakeLSN(Relation rel)
> > > 
> > >          return GetFakeLSNForUnloggedRel();
> > >      
> > >      }
> > >  
> > >  }
> > > 
> > > +
> > > +/* values from GistOptBufferingMode */
> > > +opt_enum_elt_def gistBufferingOptValues[] =
> > > +{
> > > +    {"auto", GIST_OPTION_BUFFERING_AUTO},
> > > +    {"on", GIST_OPTION_BUFFERING_ON},
> > > +    {"off", GIST_OPTION_BUFFERING_OFF},
> > > +    {(const char *) NULL}        /* list terminator */
> > > +};
> > > +
> > > +static options_spec_set *gist_relopt_specset = NULL;
> > > +
> > > +void *
> > > +gistgetreloptspecset(void)
> > > +{
> > > +    if (gist_relopt_specset)
> > > +        return gist_relopt_specset;
> > > +
> > > +    gist_relopt_specset = allocateOptionsSpecSet(NULL,
> > > +                                        
>          sizeof(GiSTOptions), 2);
> > > +
> > > +    optionsSpecSetAddInt(gist_relopt_specset, "fillfactor",
> > > +                        "Packs gist index pages only 
> to this percentage",
> > > +                             NoLock,        /* 
> No ALTER, no lock */
> > > +                             0,
> > > +                             offsetof(GiSTOptions, 
> fillfactor),
> > > +                             
> GIST_DEFAULT_FILLFACTOR,
> > > +                             GIST_MIN_FILLFACTOR, 
> 100);
> > > +
> > > +    optionsSpecSetAddEnum(gist_relopt_specset, "buffering",
> > > +                           "Enables buffering build 
> for this GiST index",
> > > +                              NoLock,        /* 
> No ALTER, no lock */
> > > +                              0,
> > > +                              
> offsetof(GiSTOptions, buffering_mode),
> > > +                              
> gistBufferingOptValues,
> > > +                              
> GIST_OPTION_BUFFERING_AUTO,
> > > +                              gettext_noop("Valid 
> values are \"on\", \"off\", and
> > > \"auto\"."));
> > > +    return gist_relopt_specset;
> > > +}
> > > diff --git a/src/backend/access/hash/hash.c
> > > b/src/backend/access/hash/hash.c index eb38104..8dc4ca7 100644
> > > --- a/src/backend/access/hash/hash.c
> > > +++ b/src/backend/access/hash/hash.c
> > > @@ -85,7 +85,6 @@ hashhandler(PG_FUNCTION_ARGS)
> > > 
> > >      amroutine->amvacuumcleanup = hashvacuumcleanup;
> > >      amroutine->amcanreturn = NULL;
> > >      amroutine->amcostestimate = hashcostestimate;
> > > 
> > > -    amroutine->amoptions = hashoptions;
> > > 
> > >      amroutine->amproperty = NULL;
> > >      amroutine->ambuildphasename = NULL;
> > >      amroutine->amvalidate = hashvalidate;
> > > 
> > > @@ -100,6 +99,7 @@ hashhandler(PG_FUNCTION_ARGS)
> > > 
> > >      amroutine->amestimateparallelscan = NULL;
> > >      amroutine->aminitparallelscan = NULL;
> > >      amroutine->amparallelrescan = NULL;
> > > 
> > > +    amroutine->amreloptspecset = hashgetreloptspecset;
> > > 
> > >      PG_RETURN_POINTER(amroutine);
> > >  
> > >  }
> > > 
> > > diff --git a/src/backend/access/hash/hashpage.c
> > > b/src/backend/access/hash/hashpage.c index 159646c..38f64ef 100644
> > > --- a/src/backend/access/hash/hashpage.c
> > > +++ b/src/backend/access/hash/hashpage.c
> > > @@ -359,6 +359,8 @@ _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 */
> > > 
> > > +//elog(WARNING, "fillfactor = %i", HashGetFillFactor(rel));
> > > +
> > > 
> > >      ffactor = HashGetTargetPageUsage(rel) / item_width;
> > >      /* keep to a sane range */
> > >      if (ffactor < 10)
> > > 
> > > diff --git a/src/backend/access/hash/hashutil.c
> > > b/src/backend/access/hash/hashutil.c index 5198728..826beab 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 "port/pg_bitutils.h"
> > >  #include "storage/buf_internals.h"
> > > 
> > > @@ -272,19 +272,6 @@ _hash_checkpage(Relation rel, Buffer buf, int flags)
> > > 
> > >      }
> > >  
> > >  }
> > > 
> > > -bytea *
> > > -hashoptions(Datum reloptions, bool validate)
> > > -{
> > > -    static const relopt_parse_elt tab[] = {
> > > -        {"fillfactor", RELOPT_TYPE_INT, offsetof(HashOptions, 
> fillfactor)},
> > > -    };
> > > -
> > > -    return (bytea *) build_reloptions(reloptions, validate,
> > > -                                      
> RELOPT_KIND_HASH,
> > > -                                      
> sizeof(HashOptions),
> > > -                                      
> tab, lengthof(tab));
> > > -}
> > > -
> > > 
> > >  /*
> > >  
> > >   * _hash_get_indextuple_hashkey - get the hash index tuple's hash key
> > >   value
> > >   */
> > > 
> > > @@ -620,3 +607,24 @@ _hash_kill_items(IndexScanDesc scan)
> > > 
> > >      else
> > >      
> > >          _hash_relbuf(rel, buf);
> > >  
> > >  }
> > > 
> > > +
> > > +static options_spec_set *hash_relopt_specset = NULL;
> > > +
> > > +void *
> > > +hashgetreloptspecset(void)
> > > +{
> > > +    if (hash_relopt_specset)
> > > +        return hash_relopt_specset;
> > > +
> > > +    hash_relopt_specset = allocateOptionsSpecSet(NULL,
> > > +                                        
>       sizeof(HashOptions), 1);
> > > +    optionsSpecSetAddInt(hash_relopt_specset, "fillfactor",
> > > +                        "Packs hash index pages only 
> to this percentage",
> > > +                             NoLock,        /* 
> No ALTER -- no lock */
> > > +                             0,
> > > +                             offsetof(HashOptions, 
> fillfactor),
> > > +                             
> HASH_DEFAULT_FILLFACTOR,
> > > +                             HASH_MIN_FILLFACTOR, 
> 100);
> > > +
> > > +    return hash_relopt_specset;
> > > +}
> > > diff --git a/src/backend/access/nbtree/nbtinsert.c
> > > b/src/backend/access/nbtree/nbtinsert.c index 7355e1d..f7b117e 100644
> > > --- a/src/backend/access/nbtree/nbtinsert.c
> > > +++ b/src/backend/access/nbtree/nbtinsert.c
> > > @@ -2745,6 +2745,8 @@ _bt_delete_or_dedup_one_page(Relation rel, Relation
> > > heapRel,> 
> > >          _bt_bottomupdel_pass(rel, buffer, heapRel, insertstate-
> >itemsz))
> > >          return;
> > > 
> > > +// elog(WARNING, "Deduplicate_items = %i", BTGetDeduplicateItems(rel));
> > > +
> > > 
> > >      /* Perform deduplication pass (when enabled and index-is-
> allequalimage)
> > >      */
> > >      if (BTGetDeduplicateItems(rel) && itup_key->allequalimage)
> > >      
> > >          _bt_dedup_pass(rel, buffer, heapRel, insertstate->itup,
> > > 
> > > diff --git a/src/backend/access/nbtree/nbtree.c
> > > b/src/backend/access/nbtree/nbtree.c index 40ad095..f171c54 100644
> > > --- a/src/backend/access/nbtree/nbtree.c
> > > +++ b/src/backend/access/nbtree/nbtree.c
> > > @@ -22,6 +22,7 @@
> > > 
> > >  #include "access/nbtxlog.h"
> > >  #include "access/relscan.h"
> > >  #include "access/xlog.h"
> > > 
> > > +#include "access/options.h"
> > > 
> > >  #include "commands/progress.h"
> > >  #include "commands/vacuum.h"
> > >  #include "miscadmin.h"
> > > 
> > > @@ -124,7 +125,6 @@ bthandler(PG_FUNCTION_ARGS)
> > > 
> > >      amroutine->amvacuumcleanup = btvacuumcleanup;
> > >      amroutine->amcanreturn = btcanreturn;
> > >      amroutine->amcostestimate = btcostestimate;
> > > 
> > > -    amroutine->amoptions = btoptions;
> > > 
> > >      amroutine->amproperty = btproperty;
> > >      amroutine->ambuildphasename = btbuildphasename;
> > >      amroutine->amvalidate = btvalidate;
> > > 
> > > @@ -139,6 +139,7 @@ bthandler(PG_FUNCTION_ARGS)
> > > 
> > >      amroutine->amestimateparallelscan = btestimateparallelscan;
> > >      amroutine->aminitparallelscan = btinitparallelscan;
> > >      amroutine->amparallelrescan = btparallelrescan;
> > > 
> > > +    amroutine->amreloptspecset = btgetreloptspecset;
> > > 
> > >      PG_RETURN_POINTER(amroutine);
> > >  
> > >  }
> > > 
> > > @@ -1418,3 +1419,37 @@ btcanreturn(Relation index, int attno)
> > > 
> > >  {
> > >  
> > >      return true;
> > >  
> > >  }
> > > 
> > > +
> > > +static options_spec_set *bt_relopt_specset = NULL;
> > > +
> > > +void *
> > > +btgetreloptspecset(void)
> > > +{
> > > +    if (bt_relopt_specset)
> > > +        return bt_relopt_specset;
> > > +
> > > +    bt_relopt_specset = allocateOptionsSpecSet(NULL,
> > > +                                        
>        sizeof(BTOptions), 3);
> > > +
> > > +    optionsSpecSetAddInt(
> > > +        bt_relopt_specset, "fillfactor",
> > > +        "Packs btree index pages only to this percentage",
> > > +        ShareUpdateExclusiveLock, /* since it applies only to later 
> inserts */
> > > +        0, offsetof(BTOptions, fillfactor),
> > > +        BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
> > > +    );
> > > +    optionsSpecSetAddReal(
> > > +        bt_relopt_specset, "vacuum_cleanup_index_scale_factor",
> > > +        "Number of tuple inserts prior to index cleanup as a fraction 
> of
> > > reltuples", +        ShareUpdateExclusiveLock,
> > > +        0, offsetof(BTOptions,vacuum_cleanup_index_scale_factor),
> > > +        -1, 0.0, 1e10
> > > +    );
> > > +    optionsSpecSetAddBool(
> > > +        bt_relopt_specset, "deduplicate_items",
> > > +        "Enables \"deduplicate items\" feature for this btree index",
> > > +        ShareUpdateExclusiveLock, /* since it applies only to later 
> inserts */
> > > +        0, offsetof(BTOptions,deduplicate_items), true
> > > +    );
> > > +    return bt_relopt_specset;
> > > +}
> > > diff --git a/src/backend/access/nbtree/nbtutils.c
> > > b/src/backend/access/nbtree/nbtutils.c index c72b456..2588a30 100644
> > > --- a/src/backend/access/nbtree/nbtutils.c
> > > +++ b/src/backend/access/nbtree/nbtutils.c
> > > @@ -18,7 +18,7 @@
> > > 
> > >  #include <time.h>
> > >  
> > >  #include "access/nbtree.h"
> > > 
> > > -#include "access/reloptions.h"
> > > +#include "storage/lock.h"
> > > 
> > >  #include "access/relscan.h"
> > >  #include "catalog/catalog.h"
> > >  #include "commands/progress.h"
> > > 
> > > @@ -2100,25 +2100,6 @@ BTreeShmemInit(void)
> > > 
> > >          Assert(found);
> > >  
> > >  }
> > > 
> > > -bytea *
> > > -btoptions(Datum reloptions, bool validate)
> > > -{
> > > -    static const relopt_parse_elt tab[] = {
> > > -        {"fillfactor", RELOPT_TYPE_INT, offsetof(BTOptions, 
> fillfactor)},
> > > -        {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
> > > -        offsetof(BTOptions, vacuum_cleanup_index_scale_factor)},
> > > -        {"deduplicate_items", RELOPT_TYPE_BOOL,
> > > -        offsetof(BTOptions, deduplicate_items)}
> > > -
> > > -    };
> > > -
> > > -    return (bytea *) build_reloptions(reloptions, validate,
> > > -                                      
> RELOPT_KIND_BTREE,
> > > -                                      
> sizeof(BTOptions),
> > > -                                      
> tab, lengthof(tab));
> > > -
> > > -}
> > > -
> > > 
> > >  /*
> > >  
> > >   *    btproperty() -- Check boolean properties of indexes.
> > >   *
> > > 
> > > diff --git a/src/backend/access/spgist/spgutils.c
> > > b/src/backend/access/spgist/spgutils.c index 03a9cd3..14429ad 100644
> > > --- a/src/backend/access/spgist/spgutils.c
> > > +++ b/src/backend/access/spgist/spgutils.c
> > > @@ -17,7 +17,7 @@
> > > 
> > >  #include "access/amvalidate.h"
> > >  #include "access/htup_details.h"
> > > 
> > > -#include "access/reloptions.h"
> > > +#include "access/options.h"
> > > 
> > >  #include "access/spgist_private.h"
> > >  #include "access/toast_compression.h"
> > >  #include "access/transam.h"
> > > 
> > > @@ -72,7 +72,6 @@ spghandler(PG_FUNCTION_ARGS)
> > > 
> > >      amroutine->amvacuumcleanup = spgvacuumcleanup;
> > >      amroutine->amcanreturn = spgcanreturn;
> > >      amroutine->amcostestimate = spgcostestimate;
> > > 
> > > -    amroutine->amoptions = spgoptions;
> > > 
> > >      amroutine->amproperty = spgproperty;
> > >      amroutine->ambuildphasename = NULL;
> > >      amroutine->amvalidate = spgvalidate;
> > > 
> > > @@ -87,6 +86,7 @@ spghandler(PG_FUNCTION_ARGS)
> > > 
> > >      amroutine->amestimateparallelscan = NULL;
> > >      amroutine->aminitparallelscan = NULL;
> > >      amroutine->amparallelrescan = NULL;
> > > 
> > > +    amroutine->amreloptspecset = spggetreloptspecset;
> > > 
> > >      PG_RETURN_POINTER(amroutine);
> > >  
> > >  }
> > > 
> > > @@ -550,6 +550,7 @@ 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.
> > >       */
> > > 
> > > +//elog(WARNING, "fillfactor = %i", SpGistGetFillFactor(index));
> > > 
> > >      needSpace += SpGistGetTargetPageFreeSpace(index);
> > >      needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
> > > 
> > > @@ -721,23 +722,6 @@ SpGistInitMetapage(Page page)
> > > 
> > >  }
> > >  
> > >  /*
> > > 
> > > - * reloptions processing for SPGiST
> > > - */
> > > -bytea *
> > > -spgoptions(Datum reloptions, bool validate)
> > > -{
> > > -    static const relopt_parse_elt tab[] = {
> > > -        {"fillfactor", RELOPT_TYPE_INT, offsetof(SpGistOptions, 
> fillfactor)},
> > > -    };
> > > -
> > > -    return (bytea *) build_reloptions(reloptions, validate,
> > > -                                      
> RELOPT_KIND_SPGIST,
> > > -                                      
> sizeof(SpGistOptions),
> > > -                                      
> tab, lengthof(tab));
> > > -
> > > -}
> > > -
> > > -/*
> > > 
> > >   * Get the space needed to store a non-null datum of the indicated type
> > >   * in an inner tuple (that is, as a prefix or node label).
> > >   * Note the result is already rounded up to a MAXALIGN boundary.
> > > 
> > > @@ -1336,3 +1320,25 @@ spgproperty(Oid index_oid, int attno,
> > > 
> > >      return true;
> > >  
> > >  }
> > > 
> > > +
> > > +static options_spec_set *spgist_relopt_specset = NULL;
> > > +
> > > +void *
> > > +spggetreloptspecset(void)
> > > +{
> > > +    if (!spgist_relopt_specset)
> > > +    {
> > > +        spgist_relopt_specset = allocateOptionsSpecSet(NULL,
> > > +                                        
>         sizeof(SpGistOptions), 1);
> > > +
> > > +        optionsSpecSetAddInt(spgist_relopt_specset, "fillfactor",
> > > +                          "Packs spgist index pages 
> only to this percentage",
> > > +                                 
> ShareUpdateExclusiveLock,        /* since it applies only
> > > +                                        
>                          * to later inserts */
> > > +                                 0,
> > > +                                 
> offsetof(SpGistOptions, fillfactor),
> > > +                                 
> SPGIST_DEFAULT_FILLFACTOR,
> > > +                                 
> SPGIST_MIN_FILLFACTOR, 100);
> > > +    }
> > > +    return spgist_relopt_specset;
> > > +}
> > > diff --git a/src/backend/commands/createas.c
> > > b/src/backend/commands/createas.c index 0982851..4f3dbb8 100644
> > > --- a/src/backend/commands/createas.c
> > > +++ b/src/backend/commands/createas.c
> > > @@ -90,6 +90,7 @@ create_ctas_internal(List *attrList, IntoClause *into)
> > > 
> > >      Datum        toast_options;
> > >      static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
> > >      ObjectAddress intoRelationAddr;
> > > 
> > > +    List       *toastDefList;
> > > 
> > >      /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED 
> VIEW
> > >      */
> > >      is_matview = (into->viewQuery != NULL);
> > > 
> > > @@ -124,14 +125,12 @@ 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);
> > > +    optionsDefListValdateNamespaces(create->options, validnsps);
> > > +    toastDefList = optionsDefListFilterNamespaces(create->options, 
> "toast");
> > > 
> > > +    toast_options = transformOptions(get_toast_relopt_spec_set(), (Datum) 
> 0,
> > > +                                     
> toastDefList, 0);
> > > 
> > >      NewRelationCreateToastTable(intoRelationAddr.objectId, 
> toast_options);
> > >      
> > >      /* Create the "view" part of a materialized view. */
> > > 
> > > diff --git a/src/backend/commands/foreigncmds.c
> > > b/src/backend/commands/foreigncmds.c index 146fa57..758ca34 100644
> > > --- a/src/backend/commands/foreigncmds.c
> > > +++ b/src/backend/commands/foreigncmds.c
> > > @@ -112,7 +112,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 c14ca27..96d465a 100644
> > > --- a/src/backend/commands/indexcmds.c
> > > +++ b/src/backend/commands/indexcmds.c
> > > @@ -19,6 +19,7 @@
> > > 
> > >  #include "access/heapam.h"
> > >  #include "access/htup_details.h"
> > >  #include "access/reloptions.h"
> > > 
> > > +#include "access/options.h"
> > > 
> > >  #include "access/sysattr.h"
> > >  #include "access/tableam.h"
> > >  #include "access/xact.h"
> > > 
> > > @@ -531,7 +532,7 @@ DefineIndex(Oid relationId,
> > > 
> > >      Form_pg_am    accessMethodForm;
> > >      IndexAmRoutine *amRoutine;
> > >      bool        amcanorder;
> > > 
> > > -    amoptions_function amoptions;
> > > +    amreloptspecset_function amreloptspecsetfn;
> > > 
> > >      bool        partitioned;
> > >      bool        safe_index;
> > >      Datum        reloptions;
> > > 
> > > @@ -837,7 +838,7 @@ DefineIndex(Oid relationId,
> > > 
> > >                          accessMethodName)));
> > >      
> > >      amcanorder = amRoutine->amcanorder;
> > > 
> > > -    amoptions = amRoutine->amoptions;
> > > +    amreloptspecsetfn = amRoutine->amreloptspecset;
> > > 
> > >      pfree(amRoutine);
> > >      ReleaseSysCache(tuple);
> > > 
> > > @@ -851,10 +852,19 @@ 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 (amreloptspecsetfn)
> > > +    {
> > > +        reloptions = transformOptions(amreloptspecsetfn(),
> > > +                                      
> (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.
> > > 
> > > @@ -1986,8 +1996,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
> > > 
> > >                      palloc0(sizeof(Datum) * indexInfo-
> >ii_NumIndexAttrs);
> > >              
> > >              indexInfo->ii_OpclassOptions[attn] =
> > > 
> > > -                transformRelOptions((Datum) 0, attribute-
> >opclassopts,
> > > -                                    
> NULL, NULL, false, false);
> > > +                optionsDefListToTextArray(attribute-
> >opclassopts);
> > > 
> > >          }
> > >          
> > >          attn++;
> > > 
> > > diff --git a/src/backend/commands/tablecmds.c
> > > b/src/backend/commands/tablecmds.c index 1c2ebe1..7f3004f 100644
> > > --- a/src/backend/commands/tablecmds.c
> > > +++ b/src/backend/commands/tablecmds.c
> > > @@ -20,6 +20,7 @@
> > > 
> > >  #include "access/heapam_xlog.h"
> > >  #include "access/multixact.h"
> > >  #include "access/reloptions.h"
> > > 
> > > +#include "access/options.h"
> > > 
> > >  #include "access/relscan.h"
> > >  #include "access/sysattr.h"
> > >  #include "access/tableam.h"
> > > 
> > > @@ -641,7 +642,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid
> > > ownerId,> 
> > >      ListCell   *listptr;
> > >      AttrNumber    attnum;
> > >      bool        partitioned;
> > > 
> > > -    static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
> > > 
> > >      Oid            ofTypeId;
> > >      ObjectAddress address;
> > >      LOCKMODE    parentLockmode;
> > > 
> > > @@ -789,19 +789,37 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid
> > > ownerId,> 
> > >      /*
> > >      
> > >       * Parse and validate reloptions, if any.
> > >       */
> > > 
> > > -    reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
> > > validnsps, -                                    
>  true, false);
> > > 
> > >      switch (relkind)
> > >      {
> > >      
> > >          case RELKIND_VIEW:
> > > -            (void) view_reloptions(reloptions, true);
> > > +            reloptions = transformOptions(
> > > +                                      
> get_view_relopt_spec_set(),
> > > +                                      
> (Datum) 0, stmt->options, 0);
> > > 
> > >              break;
> > >          
> > >          case RELKIND_PARTITIONED_TABLE:
> > > -            (void) partitioned_table_reloptions(reloptions, 
> true);
> > > +        {
> > > +            /* If it is not all listed above, then it if heap */
> > > +            char       *namespaces[] = HEAP_RELOPT_NAMESPACES;
> > > +            List       *heapDefList;
> > > +
> > > +            optionsDefListValdateNamespaces(stmt->options, 
> namespaces);
> > > +            heapDefList = optionsDefListFilterNamespaces(stmt-
> >options, NULL);
> > > +            reloptions = 
> transformOptions(get_partitioned_relopt_spec_set(),
> > > +                                      
> (Datum) 0, heapDefList, 0);
> > > 
> > >              break;
> > > 
> > > +        }
> > > 
> > >          default:
> > > -            (void) heap_reloptions(relkind, reloptions, true);
> > > +        {
> > > +            /* If it is not all listed above, 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_spec_set(),
> > > +                                      
> (Datum) 0, heapDefList, 0);
> > > +        }
> > > 
> > >      }
> > >      
> > >      if (stmt->ofTypename)
> > > 
> > > @@ -4022,7 +4040,7 @@ void
> > > 
> > >  AlterTableInternal(Oid relid, List *cmds, bool recurse)
> > >  {
> > >  
> > >      Relation    rel;
> > > 
> > > -    LOCKMODE    lockmode = AlterTableGetLockLevel(cmds);
> > > +    LOCKMODE    lockmode = AlterTableGetLockLevel(relid, cmds);
> > > 
> > >      rel = relation_open(relid, lockmode);
> > > 
> > > @@ -4064,7 +4082,7 @@ 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.
> > > 
> > > @@ -4285,9 +4303,13 @@ AlterTableGetLockLevel(List *cmds)
> > > 
> > >                                       * 
> getTables() */
> > >              
> > >              case AT_ResetRelOptions:    /* Uses MVCC in 
> getIndexes() and
> > >              
> > >                                          
>  * getTables() */
> > > 
> > > -                cmd_lockmode = 
> AlterTableGetRelOptionsLockLevel((List *) cmd->def);
> > > -                break;
> > > -
> > > +                {
> > > +                    Relation rel = relation_open(relid, 
> NoLock);  // FIXME I am not sure
> > > how wise it is +                    cmd_lockmode = 
> AlterTableGetRelOptionsLockLevel(rel,
> > > +                                        
>             castNode(List, cmd->def));
> > > +                    relation_close(rel,NoLock);
> > > +                    break;
> > > +                }
> > > 
> > >              case AT_AttachPartition:
> > >                  cmd_lockmode = ShareUpdateExclusiveLock;
> > >                  break;
> > > 
> > > @@ -8062,11 +8084,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_spec_set(),
> > > +                                  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));
> > > 
> > > @@ -13704,7 +13726,8 @@ 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;
> > > +    List       *toastDefList;
> > > +    options_parse_mode parse_mode;
> > > 
> > >      if (defList == NIL && operation != AT_ReplaceRelOptions)
> > >      
> > >          return;                    /* nothing to do 
> */
> > > 
> > > @@ -13734,27 +13757,68 @@ ATExecSetRelOptions(Relation rel, List *defList,
> > > AlterTableType operation,> 
> > >      }
> > >      
> > >      /* 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_TOASTVALUE: // FIXME why it is here???
> > > 
> > >          case RELKIND_MATVIEW:
> > > -            (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_spec_set(),
> > > +                                        
>       isnull ? (Datum) 0 : datum,
> > > +                                        
>       heapDefList, parse_mode);
> > > +            }
> > > 
> > >              break;
> > > 
> > > +
> > > 
> > >          case RELKIND_PARTITIONED_TABLE:
> > > -            (void) partitioned_table_reloptions(newOptions, 
> true);
> > > -            break;
> > > +            {
> > > +                char       *namespaces[] = 
> HEAP_RELOPT_NAMESPACES;
> > > +                List       *heapDefList;
> > > +
> > > +                optionsDefListValdateNamespaces(defList, 
> namespaces);
> > > +                heapDefList = optionsDefListFilterNamespaces(
> > > +                                        
>                      defList, NULL);
> > > +                newOptions = 
> transformOptions(get_partitioned_relopt_spec_set(),
> > > +                                        
>       isnull ? (Datum) 0 : datum,
> > > +                                        
>       heapDefList, parse_mode);
> > > +                break;
> > > +            }
> > > 
> > >          case RELKIND_VIEW:
> > > -            (void) view_reloptions(newOptions, true);
> > > -            break;
> > > +            {
> > > +
> > > +                newOptions = transformOptions(
> > > +                                      
> get_view_relopt_spec_set(),
> > > +                                      
> datum, defList, parse_mode);
> > > +                break;
> > > +            }
> > > 
> > >          case RELKIND_INDEX:
> > > 
> > >          case RELKIND_PARTITIONED_INDEX:
> > > -            (void) index_reloptions(rel->rd_indam->amoptions, 
> newOptions, true);
> > > +            if (! rel->rd_indam->amreloptspecset)
> > > +            {
> > > +                ereport(ERROR,
> > > +                        
> (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
> > > +                         errmsg("index %s does not 
> support options",
> > > +                                
> RelationGetRelationName(rel))));
> > > +                break;
> > > +            }
> > > +            parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;
> > > +            if (operation == AT_ResetRelOptions)
> > > +                parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;
> > > +            newOptions = transformOptions(
> > > +                                    rel-
> >rd_indam->amreloptspecset(),
> > > +                                        
>     isnull ? (Datum) 0 : datum,
> > > +                                        
>     defList, parse_mode);
> > > 
> > >              break;
> > >          
> > >          default:
> > >              ereport(ERROR,
> > > 
> > > @@ -13769,7 +13833,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;
> > > 
> > > @@ -13853,11 +13917,15 @@ ATExecSetRelOptions(Relation rel, List *defList,
> > > AlterTableType operation,> 
> > >                                      
> &isnull);
> > >          
> > >          }
> > > 
> > > -        newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
> > > -                                        
>  defList, "toast", validnsps, false,
> > > -                                        
>  operation == AT_ResetRelOptions);
> > > +        parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;
> > > +        if (operation == AT_ResetRelOptions)
> > > +            parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;
> > > +
> > > +        toastDefList = optionsDefListFilterNamespaces(defList, 
> "toast");
> > > 
> > > -        (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, 
> true);
> > > +        newOptions = transformOptions(get_toast_relopt_spec_set(),
> > > +                                      
> isnull ? (Datum) 0 : datum,
> > > +                                      
> toastDefList, parse_mode);
> > > 
> > >          memset(repl_val, 0, sizeof(repl_val));
> > >          memset(repl_null, false, sizeof(repl_null));
> > > 
> > > diff --git a/src/backend/commands/tablespace.c
> > > b/src/backend/commands/tablespace.c index 4b96eec..912699b 100644
> > > --- a/src/backend/commands/tablespace.c
> > > +++ b/src/backend/commands/tablespace.c
> > > @@ -345,10 +345,9 @@ 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_spec_set(),
> > > +                                        
>         (Datum) 0, stmt->options, 0);
> > > +
> > > 
> > >      if (newOptions != (Datum) 0)
> > >      
> > >          values[Anum_pg_tablespace_spcoptions - 1] = newOptions;
> > >      
> > >      else
> > > 
> > > @@ -1053,10 +1052,11 @@ 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_spec_set(),
> > > +                                  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 5564dc3..0370be7 100644
> > > --- a/src/backend/foreign/foreign.c
> > > +++ b/src/backend/foreign/foreign.c
> > > @@ -78,7 +78,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
> > > 
> > >      if (isnull)
> > >      
> > >          fdw->options = NIL;
> > >      
> > >      else
> > > 
> > > -        fdw->options = untransformRelOptions(datum);
> > > +        fdw->options = optionsTextArrayToDefList(datum);
> > > 
> > >      ReleaseSysCache(tp);
> > > 
> > > @@ -165,7 +165,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
> > > 
> > >      if (isnull)
> > >      
> > >          server->options = NIL;
> > >      
> > >      else
> > > 
> > > -        server->options = untransformRelOptions(datum);
> > > +        server->options = optionsTextArrayToDefList(datum);
> > > 
> > >      ReleaseSysCache(tp);
> > > 
> > > @@ -233,7 +233,7 @@ GetUserMapping(Oid userid, Oid serverid)
> > > 
> > >      if (isnull)
> > >      
> > >          um->options = NIL;
> > >      
> > >      else
> > > 
> > > -        um->options = untransformRelOptions(datum);
> > > +        um->options = optionsTextArrayToDefList(datum);
> > > 
> > >      ReleaseSysCache(tp);
> > > 
> > > @@ -270,7 +270,7 @@ GetForeignTable(Oid relid)
> > > 
> > >      if (isnull)
> > >      
> > >          ft->options = NIL;
> > >      
> > >      else
> > > 
> > > -        ft->options = untransformRelOptions(datum);
> > > +        ft->options = optionsTextArrayToDefList(datum);
> > > 
> > >      ReleaseSysCache(tp);
> > > 
> > > @@ -303,7 +303,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
> > > 
> > >      if (isnull)
> > >      
> > >          options = NIL;
> > >      
> > >      else
> > > 
> > > -        options = untransformRelOptions(datum);
> > > +        options = optionsTextArrayToDefList(datum);
> > > 
> > >      ReleaseSysCache(tp);
> > > 
> > > @@ -572,7 +572,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;
> > >  
> > >  }
> > > 
> > > @@ -643,7 +643,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/parser/parse_utilcmd.c
> > > b/src/backend/parser/parse_utilcmd.c index 313d7b6..1fe41b4 100644
> > > --- a/src/backend/parser/parse_utilcmd.c
> > > +++ b/src/backend/parser/parse_utilcmd.c
> > > @@ -1757,7 +1757,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation
> > > source_idx,> 
> > >          /* Add the operator class name, if non-default */
> > >          iparam->opclass = get_opclass(indclass->values[keyno], 
> keycoltype);
> > >          iparam->opclassopts =
> > > 
> > > -            untransformRelOptions(get_attoptions(source_relid, 
> keyno + 1));
> > > +            
> optionsTextArrayToDefList(get_attoptions(source_relid, keyno + 1));
> > > 
> > >          iparam->ordering = SORTBY_DEFAULT;
> > >          iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
> > > 
> > > @@ -1821,7 +1821,7 @@ generateClonedIndexStmt(RangeVar *heapRel, 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/tcop/utility.c b/src/backend/tcop/utility.c
> > > index bf085aa..d12ab1a 100644
> > > --- a/src/backend/tcop/utility.c
> > > +++ b/src/backend/tcop/utility.c
> > > @@ -1155,6 +1155,7 @@ ProcessUtilitySlow(ParseState *pstate,
> > > 
> > >                              CreateStmt *cstmt = 
> (CreateStmt *) stmt;
> > >                              Datum        
> toast_options;
> > >                              static char 
> *validnsps[] = HEAP_RELOPT_NAMESPACES;
> > > 
> > > +                            List       
> *toastDefList;
> > > 
> > >                              /* Remember 
> transformed RangeVar for LIKE */
> > >                              table_rv = cstmt-
> >relation;
> > > 
> > > @@ -1178,15 +1179,17 @@ ProcessUtilitySlow(ParseState *pstate,
> > > 
> > >                               * parse and 
> validate reloptions for the toast
> > >                               * table
> > >                               */
> > > 
> > > -                            toast_options = 
> transformRelOptions((Datum) 0,
> > > -                                        
>                         cstmt->options,
> > > -                                        
>                         "toast",
> > > -                                        
>                         validnsps,
> > > -                                        
>                         true,
> > > -                                        
>                         false);
> > > -                            (void) 
> heap_reloptions(RELKIND_TOASTVALUE,
> > > -                                        
>            toast_options,
> > > -                                        
>            true);
> > > +
> > > +                            
> optionsDefListValdateNamespaces(
> > > +                                        
>       ((CreateStmt *) stmt)->options,
> > > +                                        
>                     validnsps);
> > > +
> > > +                            toastDefList = 
> optionsDefListFilterNamespaces(
> > > +                                    
> ((CreateStmt *) stmt)->options, "toast");
> > > +
> > > +                            toast_options = 
> transformOptions(
> > > +                                       
> get_toast_relopt_spec_set(), (Datum) 0,
> > > +                                        
>                      toastDefList, 0);
> > > 
> > >                              
> NewRelationCreateToastTable(address.objectId,
> > >                              
> > >                                          
>                 toast_options);
> > > 
> > > @@ -1295,9 +1298,12 @@ 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); // FIXME!
> > > +                    if (OidIsValid(relid))
> > > +                    {
> > > +                        lockmode = 
> AlterTableGetLockLevel(relid, atstmt->cmds);
> > > +                        relid = 
> AlterTableLookupRelation(atstmt, lockmode);
> > > +                    }
> > > 
> > >                      if (OidIsValid(relid))
> > >                      {
> > >                      
> > >                          AlterTableUtilityContext 
> atcontext;
> > > 
> > > diff --git a/src/backend/utils/cache/attoptcache.c
> > > b/src/backend/utils/cache/attoptcache.c index 72d89cb..f651129 100644
> > > --- a/src/backend/utils/cache/attoptcache.c
> > > +++ b/src/backend/utils/cache/attoptcache.c
> > > @@ -16,6 +16,7 @@
> > > 
> > >   */
> > >  
> > >  #include "postgres.h"
> > > 
> > > +#include "access/options.h"
> > > 
> > >  #include "access/reloptions.h"
> > >  #include "utils/attoptcache.h"
> > >  #include "utils/catcache.h"
> > > 
> > > @@ -148,7 +149,8 @@ get_attribute_options(Oid attrelid, int attnum)
> > > 
> > >                  opts = NULL;
> > >              
> > >              else
> > >              {
> > > 
> > > -                bytea       *bytea_opts = 
> attribute_reloptions(datum, false);
> > > +                bytea   *bytea_opts = 
> optionsTextArrayToBytea(
> > > +                                    
> get_attribute_options_spec_set(), datum, 0);
> > > 
> > >                  opts = 
> MemoryContextAlloc(CacheMemoryContext,
> > >                  
> > >                                          
>   VARSIZE(bytea_opts));
> > > 
> > > diff --git a/src/backend/utils/cache/relcache.c
> > > b/src/backend/utils/cache/relcache.c index 13d9994..f22c2d9 100644
> > > --- a/src/backend/utils/cache/relcache.c
> > > +++ b/src/backend/utils/cache/relcache.c
> > > @@ -441,7 +441,7 @@ static void
> > > 
> > >  RelationParseRelOptions(Relation relation, HeapTuple tuple)
> > >  {
> > >  
> > >      bytea       *options;
> > > 
> > > -    amoptions_function amoptsfn;
> > > +    amreloptspecset_function amoptspecsetfn;
> > > 
> > >      relation->rd_options = NULL;
> > > 
> > > @@ -456,11 +456,11 @@ RelationParseRelOptions(Relation relation, HeapTuple
> > > tuple)> 
> > >          case RELKIND_VIEW:
> > >          case RELKIND_MATVIEW:
> > > 
> > >          case RELKIND_PARTITIONED_TABLE:
> > > -            amoptsfn = NULL;
> > > +            amoptspecsetfn = NULL;
> > > 
> > >              break;
> > >          
> > >          case RELKIND_INDEX:
> > > 
> > >          case RELKIND_PARTITIONED_INDEX:
> > > -            amoptsfn = relation->rd_indam->amoptions;
> > > +            amoptspecsetfn = relation->rd_indam->amreloptspecset;
> > > 
> > >              break;
> > >          
> > >          default:
> > >              return;
> > > 
> > > @@ -471,7 +471,7 @@ RelationParseRelOptions(Relation relation, HeapTuple
> > > tuple)> 
> > >       * we might not have any other for pg_class yet (consider executing 
> this
> > >       * code for pg_class itself)
> > >       */
> > > 
> > > -    options = extractRelOptions(tuple, GetPgClassDescriptor(), 
> amoptsfn);
> > > +    options = extractRelOptions(tuple, GetPgClassDescriptor(),
> > > amoptspecsetfn);> 
> > >      /*
> > >      
> > >       * 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 5870f43..87f2fa5 100644
> > > --- a/src/backend/utils/cache/spccache.c
> > > +++ b/src/backend/utils/cache/spccache.c
> > > @@ -148,7 +148,8 @@ get_tablespace(Oid spcid)
> > > 
> > >              opts = NULL;
> > >          
> > >          else
> > >          {
> > > 
> > > -            bytea       *bytea_opts = 
> tablespace_reloptions(datum, false);
> > > +            bytea *bytea_opts  = optionsTextArrayToBytea(
> > > +                                
> get_tablespace_options_spec_set(), datum, 0);
> > > 
> > >              opts = MemoryContextAlloc(CacheMemoryContext, 
> VARSIZE(bytea_opts));
> > >              memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
> > > 
> > > diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
> > > index d357ebb..b8fb6b9 100644
> > > --- a/src/include/access/amapi.h
> > > +++ b/src/include/access/amapi.h
> > > @@ -136,10 +136,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,
> > > 
> > > @@ -186,6 +182,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc
> > > scan);> 
> > >  /* restore marked scan position */
> > >  typedef void (*amrestrpos_function) (IndexScanDesc scan);
> > > 
> > > +/* get catalog of reloptions definitions */
> > > +typedef void *(*amreloptspecset_function) ();
> > > +
> > > 
> > >  /*
> > >  
> > >   * Callback function signatures - for parallel index scans.
> > >   */
> > > 
> > > @@ -263,7 +262,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 */
> > >      ambuildphasename_function ambuildphasename; /* can be NULL */
> > >      amvalidate_function amvalidate;
> > > 
> > > @@ -275,6 +273,7 @@ typedef struct IndexAmRoutine
> > > 
> > >      amendscan_function amendscan;
> > >      ammarkpos_function ammarkpos;    /* can be NULL */
> > >      amrestrpos_function amrestrpos; /* can be NULL */
> > > 
> > > +    amreloptspecset_function amreloptspecset; /* 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 4e2be13..25b3456 100644
> > > --- a/src/include/access/brin.h
> > > +++ b/src/include/access/brin.h
> > > @@ -36,6 +36,8 @@ typedef struct BrinStatsData
> > > 
> > >  #define BRIN_DEFAULT_PAGES_PER_RANGE    128
> > > 
> > > +#define BRIN_MIN_PAGES_PER_RANGE        1
> > > +#define BRIN_MAX_PAGES_PER_RANGE        131072
> > > 
> > >  #define BrinGetPagesPerRange(relation) \
> > >  
> > >      (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
> > >      
> > >                   relation->rd_rel->relam == BRIN_AM_OID), \
> > > 
> > > diff --git a/src/include/access/brin_internal.h
> > > b/src/include/access/brin_internal.h index 79440eb..a798a96 100644
> > > --- a/src/include/access/brin_internal.h
> > > +++ b/src/include/access/brin_internal.h
> > > @@ -14,6 +14,7 @@
> > > 
> > >  #include "access/amapi.h"
> > >  #include "storage/bufpage.h"
> > >  #include "utils/typcache.h"
> > > 
> > > +#include "access/options.h"
> > > 
> > >  /*
> > > 
> > > @@ -108,6 +109,7 @@ extern IndexBulkDeleteResult
> > > *brinbulkdelete(IndexVacuumInfo *info,> 
> > >  extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
> > >  
> > >                                          
>         IndexBulkDeleteResult *stats);
> > >  
> > >  extern bytea *brinoptions(Datum reloptions, bool validate);
> > > 
> > > +extern void * bringetreloptspecset (void);
> > > 
> > >  /* 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 670a40b..2b7c25c 100644
> > > --- a/src/include/access/gin_private.h
> > > +++ b/src/include/access/gin_private.h
> > > @@ -108,6 +108,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 *gingetreloptspecset(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 553d364..015b75a 100644
> > > --- a/src/include/access/gist_private.h
> > > +++ b/src/include/access/gist_private.h
> > > @@ -22,6 +22,7 @@
> > > 
> > >  #include "storage/buffile.h"
> > >  #include "utils/hsearch.h"
> > >  #include "access/genam.h"
> > > 
> > > +#include "access/reloptions.h" //FIXME! should be replaced with options.h
> > > finally> 
> > >  /*
> > >  
> > >   * Maximum number of "halves" a page can be split into in one operation.
> > > 
> > > @@ -388,6 +389,7 @@ typedef enum GistOptBufferingMode
> > > 
> > >      GIST_OPTION_BUFFERING_OFF
> > >  
> > >  } GistOptBufferingMode;
> > > 
> > > +
> > > 
> > >  /*
> > >  
> > >   * Storage type for GiST's reloptions
> > >   */
> > > 
> > > @@ -478,7 +480,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
> > > 
> > >  #define GIST_MIN_FILLFACTOR            10
> > >  #define GIST_DEFAULT_FILLFACTOR        90
> > > 
> > > -extern bytea *gistoptions(Datum reloptions, bool validate);
> > > +extern void *gistgetreloptspecset(void);
> > > 
> > >  extern bool gistproperty(Oid index_oid, int attno,
> > >  
> > >                           IndexAMProperty prop, const 
> char *propname,
> > >                           bool *res, bool *isnull);
> > > 
> > > diff --git a/src/include/access/hash.h b/src/include/access/hash.h
> > > index 1cce865..91922ef 100644
> > > --- a/src/include/access/hash.h
> > > +++ b/src/include/access/hash.h
> > > @@ -378,7 +378,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 void hashadjustmembers(Oid opfamilyoid,
> > >  
> > >                                Oid opclassoid,
> > > 
> > > @@ -470,6 +469,7 @@ extern BlockNumber
> > > _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu> 
> > >  extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket
> > >  old_bucket,>  
> > >                                          
>          uint32 lowmask, uint32 maxbucket);
> > >  
> > >  extern void _hash_kill_items(IndexScanDesc scan);
> > > 
> > > +extern void *hashgetreloptspecset(void);
> > > 
> > >  /* hash.c */
> > >  extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
> > > 
> > > diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
> > > index 30a216e..1fcb5f5 100644
> > > --- a/src/include/access/nbtree.h
> > > +++ b/src/include/access/nbtree.h
> > > @@ -1252,7 +1252,7 @@ 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 void * btgetreloptspecset (void);
> > > 
> > >  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..34e2917
> > > --- /dev/null
> > > +++ b/src/include/access/options.h
> > > @@ -0,0 +1,245 @@
> > > +/*-----------------------------------------------------------------------
> > > -- + *
> > > + * 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"
> > > +
> > > +
> > > +/* supported option types */
> > > +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_spec_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_spec_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;
> > > +
> > > +
> > > +
> > > +/*
> > > + * opt_enum_elt_def -- One member of the array of acceptable values
> > > + * of an enum reloption.
> > > + */
> > > +typedef struct opt_enum_elt_def
> > > +{
> > > +    const char *string_val;
> > > +    int            symbol_val;
> > > +} opt_enum_elt_def;
> > > +
> > > +
> > > +/* generic structure to store Option Spec information */
> > > +typedef struct option_spec_basic
> > > +{
> > > +    const char *name;            /* must be first (used as list 
> termination
> > > +                                 * marker) */
> > > +    const char *desc;
> > > +    LOCKMODE    lockmode;
> > > +    option_spec_flags flags;
> > > +    option_type type;
> > > +    int            struct_offset;    /* offset of the value in 
> Bytea representation */
> > > +}    option_spec_basic;
> > > +
> > > +
> > > +/* reloptions records for specific variable types */
> > > +typedef struct option_spec_bool
> > > +{
> > > +    option_spec_basic base;
> > > +    bool        default_val;
> > > +}    option_spec_bool;
> > > +
> > > +typedef struct option_spec_int
> > > +{
> > > +    option_spec_basic base;
> > > +    int            default_val;
> > > +    int            min;
> > > +    int            max;
> > > +}    option_spec_int;
> > > +
> > > +typedef struct option_spec_real
> > > +{
> > > +    option_spec_basic base;
> > > +    double        default_val;
> > > +    double        min;
> > > +    double        max;
> > > +}    option_spec_real;
> > > +
> > > +typedef struct option_spec_enum
> > > +{
> > > +    option_spec_basic base;
> > > +    opt_enum_elt_def *members;/* FIXME rewrite. Null terminated array of
> > > allowed values for +                                
>  * the option */
> > > +    int            default_val;    /* Number of item of 
> allowed_values array */
> > > +    const char  *detailmsg;
> > > +}    option_spec_enum;
> > > +
> > > +/* validation routines for strings */
> > > +typedef void (*validate_string_option) (const char *value);
> > > +
> > > +/*
> > > + * When storing sting reloptions, we shoud deal with special case when
> > > + * option value is not set. For fixed length options, we just copy
> > > default
> > > + * option value into the binary structure. For varlen value, there can be
> > > + * "not set" special case, with no default value offered.
> > > + * In this case we will set offset value to -1, so code that use
> > > relptions
> > > + * can deal this case. For better readability it was defined as a
> > > constant. + */
> > > +#define OPTION_STRING_VALUE_NOT_SET_OFFSET -1
> > > +
> > > +typedef struct option_spec_string
> > > +{
> > > +    option_spec_basic base;
> > > +    validate_string_option validate_cb;
> > > +    char       *default_val;
> > > +}    option_spec_string;
> > > +
> > > +typedef void (*postprocess_bytea_options_function) (void *data, bool
> > > validate); +
> > > +typedef struct options_spec_set
> > > +{
> > > +    option_spec_basic **definitions;
> > > +    int            num;            /* Number of 
> spec_set items in use */
> > > +    int            num_allocated;    /* Number of spec_set 
> items allocated */
> > > +    bool        forbid_realloc; /* If number of items of the 
> spec_set were
> > > +                                 * strictly 
> set to certain value do no allow
> > > +                                 * adding 
> more idems */
> > > +    Size        struct_size;    /* Size of a structure for 
> options in binary
> > > +                                 * 
> representation */
> > > +    postprocess_bytea_options_function postprocess_fun; /* This function 
> is
> > > +                                        
>                  * called after options
> > > +                                        
>                  * were converted in
> > > +                                        
>                  * Bytea represenation.
> > > +                                        
>                  * Can be used for extra
> > > +                                        
>                  * validation and so on */
> > > +    char       *namespace;        /* spec_set is used for options 
> from this
> > > +                                 * namespase 
> */
> > > +}    options_spec_set;
> > > +
> > > +
> > > +/* holds an option value parsed or unparsed */
> > > +typedef struct option_value
> > > +{
> > > +    option_spec_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;
> > > +
> > > +
> > > +
> > > +
> > > +/*
> > > + * Options spec_set related functions
> > > + */
> > > +extern options_spec_set *allocateOptionsSpecSet(const char *namespace,
> > > +                                 int 
> size_of_bytea, int num_items_expected);
> > > +extern void optionsSpecSetAddBool(options_spec_set * spec_set, const char
> > > *name, +                  const char *desc, LOCKMODE 
> lockmode, option_spec_flags
> > > flags, +                                    
> int struct_offset, bool default_val);
> > > +extern void optionsSpecSetAddInt(options_spec_set * spec_set, const char
> > > *name, +                    const char *desc, LOCKMODE 
> lockmode, option_spec_flags
> > > flags, +                    int struct_offset, int 
> default_val, int min_val, int
> > > max_val); +extern void optionsSpecSetAddReal(options_spec_set * spec_set,
> > > const char *name, +          const char *desc, LOCKMODE lockmode,
> > > option_spec_flags flags, +      int struct_offset, double default_val,
> > > double min_val, double max_val); +extern void
> > > optionsSpecSetAddEnum(options_spec_set * spec_set,
> > > +                          const char *name, const 
> char *desc, LOCKMODE lockmode,
> > > option_spec_flags flags, +            int struct_offset, 
> opt_enum_elt_def*
> > > members, int default_val, const char *detailmsg); +extern void
> > > optionsSpecSetAddString(options_spec_set * spec_set, const char *name,
> > > +          const char *desc, LOCKMODE lockmode, option_spec_flags flags, 
> +int
> > > struct_offset, const char *default_val, validate_string_option
> > > validator); +
> > > +
> > > +/*
> > > + * This macro allows to get string option value from bytea
> > > representation.
> > > + * "optstruct" - is a structure that is stored in bytea options
> > > representation + * "member" - member of this structure that has string
> > > option value + * (actually string values are stored in bytea after the
> > > structure, and + * and "member" will contain an offset to this value.
> > > This macro do all + * the math
> > > + */
> > > +#define GET_STRING_OPTION(optstruct, member) \
> > > +    ((optstruct)->member == OPTION_STRING_VALUE_NOT_SET_OFFSET ? NULL : \
> > > +     (char *)(optstruct) + (optstruct)->member)
> > > +
> > > +/*
> > > + * Functions related to option convertation, parsing, manipulation
> > > + * and validation
> > > + */
> > > +extern void optionsDefListValdateNamespaces(List *defList,
> > > +                                char 
> **allowed_namespaces);
> > > +extern List *optionsDefListFilterNamespaces(List *defList, const char
> > > *namespace); +extern List *optionsTextArrayToDefList(Datum options);
> > > +extern Datum optionsDefListToTextArray(List *defList);
> > > +/*
> > > + * Meta functions that uses functions above to get options for relations,
> > > + * tablespaces, views and so on
> > > + */
> > > +
> > > +extern bytea *optionsTextArrayToBytea(options_spec_set * spec_set, Datum
> > > data, +                                    
>                             bool validate);
> > > +extern Datum transformOptions(options_spec_set * spec_set, 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 7c5fbeb..21b91df 100644
> > > --- a/src/include/access/reloptions.h
> > > +++ b/src/include/access/reloptions.h
> > > @@ -22,6 +22,7 @@
> > > 
> > >  #include "access/amapi.h"
> > >  #include "access/htup.h"
> > >  #include "access/tupdesc.h"
> > > 
> > > +#include "access/options.h"
> > > 
> > >  #include "nodes/pg_list.h"
> > >  #include "storage/lock.h"
> > > 
> > > @@ -110,20 +111,10 @@ typedef struct relopt_real
> > > 
> > >      double        max;
> > >  
> > >  } relopt_real;
> > > 
> > > -/*
> > > - * relopt_enum_elt_def -- One member of the array of acceptable values
> > > - * of an enum reloption.
> > > - */
> > > -typedef struct relopt_enum_elt_def
> > > -{
> > > -    const char *string_val;
> > > -    int            symbol_val;
> > > -} relopt_enum_elt_def;
> > > -
> > > 
> > >  typedef struct relopt_enum
> > >  {
> > >  
> > >      relopt_gen    gen;
> > > 
> > > -    relopt_enum_elt_def *members;
> > > +    opt_enum_elt_def *members;
> > > 
> > >      int            default_val;
> > >      const char *detailmsg;
> > >      /* null-terminated array of members */
> > > 
> > > @@ -167,6 +158,7 @@ typedef struct local_relopts
> > > 
> > >      List       *options;        /* list of local_relopt 
> definitions */
> > >      List       *validators;        /* list of relopts_validator 
> callbacks */
> > >      Size        relopt_struct_size; /* size of parsed bytea 
> structure */
> > > 
> > > +    options_spec_set * spec_set; /* FIXME */
> > > 
> > >  } local_relopts;
> > >  
> > >  /*
> > > 
> > > @@ -179,21 +171,6 @@ typedef struct local_relopts
> > > 
> > >      ((optstruct)->member == 0 ? NULL : \
> > >      
> > >       (char *)(optstruct) + (optstruct)->member)
> > > 
> > > -extern relopt_kind add_reloption_kind(void);
> > > -extern void add_bool_reloption(bits32 kinds, const char *name, const char
> > > *desc, -                               bool 
> default_val, LOCKMODE lockmode);
> > > -extern void add_int_reloption(bits32 kinds, const char *name, const char
> > > *desc, -                              int 
> default_val, int min_val, int max_val,
> > > -                              LOCKMODE 
> lockmode);
> > > -extern void add_real_reloption(bits32 kinds, const char *name, const char
> > > *desc, -                               double 
> default_val, double min_val, double max_val,
> > > -                               LOCKMODE 
> lockmode);
> > > -extern void add_enum_reloption(bits32 kinds, const char *name, const char
> > > *desc, -                               
> relopt_enum_elt_def *members, int default_val,
> > > -                               const char 
> *detailmsg, LOCKMODE lockmode);
> > > -extern void add_string_reloption(bits32 kinds, const char *name, const
> > > char *desc, -                                 
> const char *default_val, validate_string_relopt
> > > validator, -                                 
> LOCKMODE lockmode);
> > > 
> > >  extern void init_local_reloptions(local_relopts *opts, Size
> > >  relopt_struct_size); extern void
> > >  register_reloptions_validator(local_relopts *opts,
> > > 
> > > @@ -210,7 +187,7 @@ extern void add_local_real_reloption(local_relopts
> > > *opts, const char *name,> 
> > >                                       int 
> offset);
> > >  
> > >  extern void add_local_enum_reloption(local_relopts *relopts,
> > >  
> > >                                       
> const char *name, const char *desc,
> > > 
> > > -                                     
> relopt_enum_elt_def *members,
> > > +                                     
> opt_enum_elt_def *members,
> > > 
> > >                                       int 
> default_val, const char *detailmsg,
> > >                                       int 
> offset);
> > >  
> > >  extern void add_local_string_reloption(local_relopts *opts, const char
> > >  *name,> 
> > > @@ -219,29 +196,17 @@ extern void add_local_string_reloption(local_relopts
> > > *opts, const char *name,> 
> > >                                         
> validate_string_relopt validator,
> > >                                         
> fill_string_relopt filler, int offset);
> > > 
> > > -extern Datum transformRelOptions(Datum oldOptions, List *defList,
> > > -                                 const char 
> *namspace, char *validnsps[],
> > > -                                 bool 
> acceptOidsOff, bool isReset);
> > > -extern List *untransformRelOptions(Datum options);
> > > 
> > >  extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
> > > 
> > > -                                
> amoptions_function amoptions);
> > > -extern void *build_reloptions(Datum reloptions, bool validate,
> > > -                              relopt_kind kind,
> > > -                              Size 
> relopt_struct_size,
> > > -                              const 
> relopt_parse_elt *relopt_elems,
> > > -                              int 
> num_relopt_elems);
> > > +                                
> amreloptspecset_function amoptions_def_set);
> > > 
> > >  extern void *build_local_reloptions(local_relopts *relopts, Datum
> > >  options,
> > >  
> > >                                      bool 
> validate);
> > > 
> > > -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 *partitioned_table_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);
> > > +options_spec_set *get_heap_relopt_spec_set(void);
> > > +options_spec_set *get_toast_relopt_spec_set(void);
> > > +options_spec_set *get_partitioned_relopt_spec_set(void);
> > > +options_spec_set *get_view_relopt_spec_set(void);
> > > +options_spec_set *get_attribute_options_spec_set(void);
> > > +options_spec_set *get_tablespace_options_spec_set(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 2eb2f42..d9a9b2d 100644
> > > --- a/src/include/access/spgist.h
> > > +++ b/src/include/access/spgist.h
> > > @@ -189,9 +189,6 @@ typedef struct spgLeafConsistentOut
> > > 
> > >  } spgLeafConsistentOut;
> > > 
> > > -/* spgutils.c */
> > > -extern bytea *spgoptions(Datum reloptions, bool validate);
> > > -
> > > 
> > >  /* spginsert.c */
> > >  extern IndexBuildResult *spgbuild(Relation heap, Relation index,
> > >  
> > >                                    struct 
> IndexInfo *indexInfo);
> > > 
> > > diff --git a/src/include/access/spgist_private.h
> > > b/src/include/access/spgist_private.h index 40d3b71..dd9a05a 100644
> > > --- a/src/include/access/spgist_private.h
> > > +++ b/src/include/access/spgist_private.h
> > > @@ -529,6 +529,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistState
> > > *state, Page page,> 
> > >  extern bool spgproperty(Oid index_oid, int attno,
> > >  
> > >                          IndexAMProperty prop, const 
> char *propname,
> > >                          bool *res, bool *isnull);
> > > 
> > > +extern void *spggetreloptspecset(void);
> > > 
> > >  /* spgdoinsert.c */
> > >  extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
> > > 
> > > diff --git a/src/include/commands/tablecmds.h
> > > b/src/include/commands/tablecmds.h index 336549c..3f87f98 100644
> > > --- a/src/include/commands/tablecmds.h
> > > +++ b/src/include/commands/tablecmds.h
> > > @@ -34,7 +34,7 @@ extern Oid    AlterTableLookupRelation(AlterTableStmt
> > > *stmt, LOCKMODE lockmode);> 
> > >  extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
> > >  
> > >                         struct AlterTableUtilityContext 
> *context);
> > > 
> > > -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/test/modules/dummy_index_am/dummy_index_am.c
> > > b/src/test/modules/dummy_index_am/dummy_index_am.c index
> > > 5365b063..80b39e8 100644
> > > --- a/src/test/modules/dummy_index_am/dummy_index_am.c
> > > +++ b/src/test/modules/dummy_index_am/dummy_index_am.c
> > > @@ -14,7 +14,7 @@
> > > 
> > >  #include "postgres.h"
> > >  
> > >  #include "access/amapi.h"
> > > 
> > > -#include "access/reloptions.h"
> > > +#include "access/options.h"
> > > 
> > >  #include "catalog/index.h"
> > >  #include "commands/vacuum.h"
> > >  #include "nodes/pathnodes.h"
> > > 
> > > @@ -25,12 +25,6 @@ PG_MODULE_MAGIC;
> > > 
> > >  void        _PG_init(void);
> > > 
> > > -/* parse table for fillRelOptions */
> > > -relopt_parse_elt di_relopt_tab[6];
> > > -
> > > -/* Kind of relation options for dummy index */
> > > -relopt_kind di_relopt_kind;
> > > -
> > > 
> > >  typedef enum DummyAmEnum
> > >  {
> > >  
> > >      DUMMY_AM_ENUM_ONE,
> > > 
> > > @@ -49,7 +43,7 @@ typedef struct DummyIndexOptions
> > > 
> > >      int            option_string_null_offset;
> > >  
> > >  }            DummyIndexOptions;
> > > 
> > > -relopt_enum_elt_def dummyAmEnumValues[] =
> > > +opt_enum_elt_def dummyAmEnumValues[] =
> > > 
> > >  {
> > >  
> > >      {"one", DUMMY_AM_ENUM_ONE},
> > >      {"two", DUMMY_AM_ENUM_TWO},
> > > 
> > > @@ -63,77 +57,85 @@ PG_FUNCTION_INFO_V1(dihandler);
> > > 
> > >   * Validation function for string relation options.
> > >   */
> > >  
> > >  static void
> > > 
> > > -validate_string_option(const char *value)
> > > +divalidate_string_option(const char *value)
> > > 
> > >  {
> > >  
> > >      ereport(NOTICE,
> > >      
> > >              (errmsg("new option value for string parameter %s",
> > >              
> > >                      value ? value : "NULL")));
> > >  
> > >  }
> > > 
> > > -/*
> > > - * This function creates a full set of relation option types,
> > > - * with various patterns.
> > > - */
> > > -static void
> > > -create_reloptions_table(void)
> > > +static options_spec_set *di_relopt_specset = NULL;
> > > +void * digetreloptspecset(void);
> > > +
> > > +void *
> > > +digetreloptspecset(void)
> > > 
> > >  {
> > > 
> > > -    di_relopt_kind = add_reloption_kind();
> > > -
> > > -    add_int_reloption(di_relopt_kind, "option_int",
> > > -                      "Integer option for 
> dummy_index_am",
> > > -                      10, -10, 100, 
> AccessExclusiveLock);
> > > -    di_relopt_tab[0].optname = "option_int";
> > > -    di_relopt_tab[0].opttype = RELOPT_TYPE_INT;
> > > -    di_relopt_tab[0].offset = offsetof(DummyIndexOptions, option_int);
> > > -
> > > -    add_real_reloption(di_relopt_kind, "option_real",
> > > -                       "Real option for dummy_index_am",
> > > -                       3.1415, -10, 100, 
> AccessExclusiveLock);
> > > -    di_relopt_tab[1].optname = "option_real";
> > > -    di_relopt_tab[1].opttype = RELOPT_TYPE_REAL;
> > > -    di_relopt_tab[1].offset = offsetof(DummyIndexOptions, option_real);
> > > -
> > > -    add_bool_reloption(di_relopt_kind, "option_bool",
> > > -                       "Boolean option for 
> dummy_index_am",
> > > -                       true, AccessExclusiveLock);
> > > -    di_relopt_tab[2].optname = "option_bool";
> > > -    di_relopt_tab[2].opttype = RELOPT_TYPE_BOOL;
> > > -    di_relopt_tab[2].offset = offsetof(DummyIndexOptions, option_bool);
> > > -
> > > -    add_enum_reloption(di_relopt_kind, "option_enum",
> > > -                       "Enum option for dummy_index_am",
> > > -                       dummyAmEnumValues,
> > > -                       DUMMY_AM_ENUM_ONE,
> > > -                       "Valid values are \"one\" and 
> \"two\".",
> > > -                       AccessExclusiveLock);
> > > -    di_relopt_tab[3].optname = "option_enum";
> > > -    di_relopt_tab[3].opttype = RELOPT_TYPE_ENUM;
> > > -    di_relopt_tab[3].offset = offsetof(DummyIndexOptions, option_enum);
> > > -
> > > -    add_string_reloption(di_relopt_kind, "option_string_val",
> > > -                         "String option for 
> dummy_index_am with non-NULL default",
> > > -                         "DefaultValue", 
> &validate_string_option,
> > > -                         AccessExclusiveLock);
> > > -    di_relopt_tab[4].optname = "option_string_val";
> > > -    di_relopt_tab[4].opttype = RELOPT_TYPE_STRING;
> > > -    di_relopt_tab[4].offset = offsetof(DummyIndexOptions,
> > > -                                       
> option_string_val_offset);
> > > +    if (di_relopt_specset)
> > > +        return di_relopt_specset;
> > > +
> > > +    di_relopt_specset = allocateOptionsSpecSet(NULL,
> > > +                                        
>        sizeof(DummyIndexOptions), 6);
> > > +
> > > +    optionsSpecSetAddInt(
> > > +        di_relopt_specset, "option_int",
> > > +        "Integer option for dummy_index_am",
> > > +        AccessExclusiveLock,
> > > +        0, offsetof(DummyIndexOptions, option_int),
> > > +        10, -10, 100
> > > +    );
> > > +
> > > +
> > > +    optionsSpecSetAddReal(
> > > +        di_relopt_specset, "option_real",
> > > +        "Real option for dummy_index_am",
> > > +        AccessExclusiveLock,
> > > +        0, offsetof(DummyIndexOptions, option_real),
> > > +        3.1415, -10, 100
> > > +    );
> > > +
> > > +    optionsSpecSetAddBool(
> > > +        di_relopt_specset, "option_bool",
> > > +        "Boolean option for dummy_index_am",
> > > +        AccessExclusiveLock,
> > > +        0, offsetof(DummyIndexOptions, option_bool), true
> > > +    );
> > > +
> > > +    optionsSpecSetAddEnum(di_relopt_specset, "option_enum",
> > > +        "Enum option for dummy_index_am",
> > > +        AccessExclusiveLock,
> > > +        0,
> > > +        offsetof(DummyIndexOptions, option_enum),
> > > +        dummyAmEnumValues,
> > > +        DUMMY_AM_ENUM_ONE,
> > > +        "Valid values are \"one\" and \"two\"."
> > > +    );
> > > +
> > > +    optionsSpecSetAddString(di_relopt_specset, "option_string_val",
> > > +        "String option for dummy_index_am with non-NULL default",
> > > +        AccessExclusiveLock,
> > > +        0,
> > > +        offsetof(DummyIndexOptions, option_string_val_offset),
> > > +        "DefaultValue", &divalidate_string_option
> > > +    );
> > > 
> > >      /*
> > >      
> > >       * String option for dummy_index_am with NULL default, and without
> > >       * description.
> > >       */
> > > 
> > > -    add_string_reloption(di_relopt_kind, "option_string_null",
> > > -                         NULL,    /* description */
> > > -                         NULL, 
> &validate_string_option,
> > > -                         AccessExclusiveLock);
> > > -    di_relopt_tab[5].optname = "option_string_null";
> > > -    di_relopt_tab[5].opttype = RELOPT_TYPE_STRING;
> > > -    di_relopt_tab[5].offset = offsetof(DummyIndexOptions,
> > > -                                       
> option_string_null_offset);
> > > +
> > > +    optionsSpecSetAddString(di_relopt_specset, "option_string_null",
> > > +        NULL,    /* description */
> > > +        AccessExclusiveLock,
> > > +        0,
> > > +        offsetof(DummyIndexOptions, option_string_null_offset),
> > > +        NULL, &divalidate_string_option
> > > +    );
> > > +
> > > +    return di_relopt_specset;
> > > 
> > >  }
> > > 
> > > +
> > > 
> > >  /*
> > >  
> > >   * Build a new index.
> > >   */
> > > 
> > > @@ -219,19 +221,6 @@ dicostestimate(PlannerInfo *root, IndexPath *path,
> > > double loop_count,> 
> > >  }
> > >  
> > >  /*
> > > 
> > > - * Parse relation options for index AM, returning a DummyIndexOptions
> > > - * structure filled with option values.
> > > - */
> > > -static bytea *
> > > -dioptions(Datum reloptions, bool validate)
> > > -{
> > > -    return (bytea *) build_reloptions(reloptions, validate,
> > > -                                      
> di_relopt_kind,
> > > -                                      
> sizeof(DummyIndexOptions),
> > > -                                      
> di_relopt_tab, lengthof(di_relopt_tab));
> > > -}
> > > -
> > > -/*
> > > 
> > >   * Validator for index AM.
> > >   */
> > >  
> > >  static bool
> > > 
> > > @@ -308,7 +297,6 @@ dihandler(PG_FUNCTION_ARGS)
> > > 
> > >      amroutine->amvacuumcleanup = divacuumcleanup;
> > >      amroutine->amcanreturn = NULL;
> > >      amroutine->amcostestimate = dicostestimate;
> > > 
> > > -    amroutine->amoptions = dioptions;
> > > 
> > >      amroutine->amproperty = NULL;
> > >      amroutine->ambuildphasename = NULL;
> > >      amroutine->amvalidate = divalidate;
> > > 
> > > @@ -322,12 +310,7 @@ dihandler(PG_FUNCTION_ARGS)
> > > 
> > >      amroutine->amestimateparallelscan = NULL;
> > >      amroutine->aminitparallelscan = NULL;
> > >      amroutine->amparallelrescan = NULL;
> > > 
> > > +    amroutine->amreloptspecset = digetreloptspecset;
> > > 
> > >      PG_RETURN_POINTER(amroutine);
> > >  
> > >  }
> > > 
> > > -
> > > -void
> > > -_PG_init(void)
> > > -{
> > > -    create_reloptions_table();
> > > -}
> 
> 
> 
> 

-- 
  Bruce Momjian  <bruce@momjian.us>        https://momjian.us
  EDB                                      https://enterprisedb.com

  If only the physical world exists, free will is an illusion.