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

From: Bruce Momjian <bruce(at)momjian(dot)us>
To: Nikolay Shaplov <dhyan(at)nataraj(dot)su>
Cc: pgsql-hackers(at)lists(dot)postgresql(dot)org
Subject: Re: Suggestion: Unified options API. Need help from core team
Date: 2021-11-29 18:08:19
Message-ID: 20211129180819.GA3516@momjian.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

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

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

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

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

--
Bruce Momjian <bruce(at)momjian(dot)us> https://momjian.us
EDB https://enterprisedb.com

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

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Andres Freund 2021-11-29 18:10:51 Re: Separate out FileSet from SharedFileSet (was Re: pgsql: pgstat: Bring up pgstat in BaseInit() to fix uninitialized use o)
Previous Message Bossart, Nathan 2021-11-29 18:06:22 Re: XMAX_LOCK_ONLY and XMAX_COMMITTED (fk/multixact code)