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