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