Re: Suggestion: Unified options API. Need help from core team - Mailing list pgsql-hackers

From Nikolay Shaplov
Subject Re: Suggestion: Unified options API. Need help from core team
Date
Msg-id 1649585.qvZ8LAfTsV@thinkpad-pgpro
Whole thread Raw
In response to Re: Suggestion: Unified options API. Need help from core team  (Bruce Momjian <bruce@momjian.us>)
Responses Re: Suggestion: Unified options API. Need help from core team
List pgsql-hackers
В письме от вторник, 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();
> > -}







pgsql-hackers by date:

Previous
From: Ken Kato
Date:
Subject: Re: [PATCH] ALTER tab completion
Next
From: Dilip Kumar
Date:
Subject: Re: Add connection active, idle time to pg_stat_activity