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