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

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

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

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

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

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

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

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

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

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

> > ShareUpdateExclusiveLock,
> > + 0, offsetof(AttributeOpts, n_distinct), 0, -1.0,
DBL_MAX);
> >
> > - foreach(lc, relopts->validators)
> > - ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
> > -
> > - if (elems)
> > - pfree(elems);
> > + optionsSpecSetAddReal(attribute_options_spec_set,
> > + "n_distinct_inherited",
> > + "Sets the planner's
estimate of the number of distinct values
> > appearing in a column (including child relations).", +

> > ShareUpdateExclusiveLock,
> > + 0, offsetof(AttributeOpts, n_distinct_inherited), 0, -1.0, DBL_MAX);
> >
> > - return opts;
> > + return attribute_options_spec_set;
> >
> > }
> >
> > -/*
> > - * Option parser for partitioned tables
> > - */
> > -bytea *
> > -partitioned_table_reloptions(Datum reloptions, bool validate)
> > -{
> > - /*
> > - * There are no options for partitioned tables yet, but this is able
to
> > do - * some validation.
> > - */
> > - return (bytea *) build_reloptions(reloptions, validate,
> > -
RELOPT_KIND_PARTITIONED,
> > - 0,
NULL, 0);
> > -}
> >
> > /*
> >
> > - * Option parser for views
> > - */
> > -bytea *
> > -view_reloptions(Datum reloptions, bool validate)
> > -{
> > - static const relopt_parse_elt tab[] = {
> > - {"security_barrier", RELOPT_TYPE_BOOL,
> > - offsetof(ViewOptions, security_barrier)},
> > - {"check_option", RELOPT_TYPE_ENUM,
> > - offsetof(ViewOptions, check_option)}
> > - };
> > -
> > - return (bytea *) build_reloptions(reloptions, validate,
> > -
RELOPT_KIND_VIEW,
> > -
sizeof(ViewOptions),
> > -
tab, lengthof(tab));
> > -}
> > + * get_tablespace_options_spec_set
> > + * Returns an options spec set for tablespaces
> > +*/
> > +static options_spec_set *tablespace_options_spec_set = NULL;
> >
> > -/*
> > - * Parse options for heaps, views and toast tables.
> > - */
> > -bytea *
> > -heap_reloptions(char relkind, Datum reloptions, bool validate)
> > +options_spec_set *
> > +get_tablespace_options_spec_set(void)
> >
> > {
> >
> > - StdRdOptions *rdopts;
> > -
> > - switch (relkind)
> > + if (!tablespace_options_spec_set)
> >
> > {
> >
> > - case RELKIND_TOASTVALUE:
> > - rdopts = (StdRdOptions *)
> > - default_reloptions(reloptions, validate,
RELOPT_KIND_TOAST);
> > - if (rdopts != NULL)
> > - {
> > - /* adjust default-only parameters for TOAST
relations */
> > - rdopts->fillfactor = 100;
> > - rdopts->autovacuum.analyze_threshold = -1;
> > - rdopts->autovacuum.analyze_scale_factor =
-1;
> > - }
> > - return (bytea *) rdopts;
> > - case RELKIND_RELATION:
> > - case RELKIND_MATVIEW:
> > - return default_reloptions(reloptions, validate,
RELOPT_KIND_HEAP);
> > - default:
> > - /* other relkinds are not supported */
> > - return NULL;
> > - }
> > -}
> > -
> > -
> > -/*
> > - * Parse options for indexes.
> > - *
> > - * amoptions index AM's option parser function
> > - * reloptions options as text[] datum
> > - * validate error flag
> > - */
> > -bytea *
> > -index_reloptions(amoptions_function amoptions, Datum reloptions, bool
> > validate) -{
> > - Assert(amoptions != NULL);
> > + tablespace_options_spec_set = allocateOptionsSpecSet(NULL,
> > +
sizeof(TableSpaceOpts), 4);
> >
> > - /* Assume function is strict */
> > - if (!PointerIsValid(DatumGetPointer(reloptions)))
> > - return NULL;
> > + optionsSpecSetAddReal(tablespace_options_spec_set,
> > +
"random_page_cost",
> > + "Sets the
planner's estimate of the cost of a nonsequentially
> > fetched disk page", +
ShareUpdateExclusiveLock,
> > + 0, offsetof(TableSpaceOpts, random_page_cost), -1,
0.0, DBL_MAX);
> >
> > - return amoptions(reloptions, validate);
> > -}
> > + optionsSpecSetAddReal(tablespace_options_spec_set,
"seq_page_cost",
> > + "Sets the
planner's estimate of the cost of a sequentially
> > fetched disk page", +
ShareUpdateExclusiveLock,
> > + 0, offsetof(TableSpaceOpts, seq_page_cost), -1,
0.0, DBL_MAX);
> >
> > -/*
> > - * Option parser for attribute reloptions
> > - */
> > -bytea *
> > -attribute_reloptions(Datum reloptions, bool validate)
> > -{
> > - static const relopt_parse_elt tab[] = {
> > - {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts,
n_distinct)},
> > - {"n_distinct_inherited", RELOPT_TYPE_REAL,
offsetof(AttributeOpts,
> > n_distinct_inherited)} - };
> > -
> > - return (bytea *) build_reloptions(reloptions, validate,
> > -
RELOPT_KIND_ATTRIBUTE,
> > -
sizeof(AttributeOpts),
> > -
tab, lengthof(tab));
> > -}
> > + optionsSpecSetAddInt(tablespace_options_spec_set,
> > +
"effective_io_concurrency",
> > + "Number of
simultaneous requests that can be handled efficiently
> > by the disk subsystem", +
ShareUpdateExclusiveLock,
> > + 0, offsetof(TableSpaceOpts,
effective_io_concurrency),
> > +#ifdef USE_PREFETCH
> > + -1, 0,
MAX_IO_CONCURRENCY
> > +#else
> > + 0, 0, 0
> > +#endif
> > + );
> >
> > -/*
> > - * Option parser for tablespace reloptions
> > - */
> > -bytea *
> > -tablespace_reloptions(Datum reloptions, bool validate)
> > -{
> > - static const relopt_parse_elt tab[] = {
> > - {"random_page_cost", RELOPT_TYPE_REAL,
offsetof(TableSpaceOpts,
> > random_page_cost)}, - {"seq_page_cost", RELOPT_TYPE_REAL,
> > offsetof(TableSpaceOpts, seq_page_cost)}, -
{"effective_io_concurrency",
> > RELOPT_TYPE_INT, offsetof(TableSpaceOpts, effective_io_concurrency)},
> > - {"maintenance_io_concurrency", RELOPT_TYPE_INT,
> > offsetof(TableSpaceOpts, maintenance_io_concurrency)} - };
> > -
> > - return (bytea *) build_reloptions(reloptions, validate,
> > -
RELOPT_KIND_TABLESPACE,
> > -
sizeof(TableSpaceOpts),
> > -
tab, lengthof(tab));
> > + optionsSpecSetAddInt(tablespace_options_spec_set,
> > +
"maintenance_io_concurrency",
> > + "Number of
simultaneous requests that can be handled efficiently
> > by the disk subsystem for maintenance work.", +

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

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Dilip Kumar 2021-11-26 08:27:28 Re: Add connection active, idle time to pg_stat_activity
Previous Message Ken Kato 2021-11-26 08:00:44 Re: [PATCH] ALTER tab completion