From: Nikolay Shaplov <dhyan@nataraj.su>
To: Andres Freund <andres@anarazel.de>
To: pgsql-hackers@lists.postgresql.org
Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Cc: Alvaro Herrera <alvherre@2ndquadrant.com>
Cc: Michael Paquier <michael@paquier.xyz>
Cc: Amit Langote <amitlangote09@gmail.com>
Cc: Nikolay Shaplov <dhyan@nataraj.su>
Subject: Re: [PATCH] New [relation] option engine
Date: Sun, 27 Mar 2022 15:19:41 +0300
Message-ID: <40206419.8isWRrtcCr@thinkpad-pgpro> (raw)
In-Reply-To: <4142565.V3TJ1GsxxI@thinkpad-pgpro>
References: <3766675.7eaCOWfIcx@thinkpad-pgpro>
<20220322004610.7pqwd35up76xrpuv@alap3.anarazel.de>
<4142565.V3TJ1GsxxI@thinkpad-pgpro>
A minor fix to get rid of maybe-uninitialized warnings.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
[text/x-patch] new_options_take_two_v01b.diff (192.2K, ../40206419.8isWRrtcCr@thinkpad-pgpro/2-new_options_take_two_v01b.diff)
download | inline diff:diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.hindex 9966f3f46e..9a009bdbb3 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.cindex a434cf93ef..285f1a9c9c 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,52 +34,12 @@
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.
*/
@@ -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,37 @@ 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), false, 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+ * not effective */+ offsetof(BloomOptions, bloomLength), NULL,+ 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+ * not effective */+ offsetof(BloomOptions, bitSize[i]), NULL,+ DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS);+ }+ return bl_relopt_specset;
}
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.outindex dae12a7d3e..e79456dbce 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 lengthdiff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sqlindex 4733e1e705..0bfc767faf 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.cindex a06d4bd12d..d327b880af 100644--- a/contrib/dblink/dblink.c+++ b/contrib/dblink/dblink.c@@ -1978,7 +1978,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.cindex db08593d97..909b82f115 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.cindex 572591a558..c56457140f 100644--- a/contrib/postgres_fdw/option.c+++ b/contrib/postgres_fdw/option.c@@ -73,7 +73,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.cindex 4366010768..addc239dfc 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.
@@ -120,7 +118,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;
@@ -135,6 +132,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)
return 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.
@@ -1758,3 +1739,30 @@ 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), false, 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 */+ offsetof(BrinOptions, pagesPerRange), NULL,+ 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,+ offsetof(BrinOptions, autosummarize), NULL,+ false);+ return brin_relopt_specset;+}+diff --git a/src/backend/access/common/Makefile b/src/backend/access/common/Makefileindex b9aff0ccfd..78c9c5a8d2 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.cnew file mode 100644index 0000000000..3df9b02e93--- /dev/null+++ b/src/backend/access/common/options.c@@ -0,0 +1,1370 @@+/*-------------------------------------------------------------------------+ *+ * options.c+ * An uniform, context-free API for processing name=value options. Used+ * to process relation options (reloptions), attribute options, opclass+ * options, etc.+ *+ * Portions Copyright (c) 1996-2022, 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. Implemented 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 "specification" instead of "definition" because therm+ * "definition" is used for objects that came from syntax parser. So to avoid+ * confusion here we have Option Specifications, and all "definitions" are from+ * parser.+ */++/*+ * OPTION VALUES REPRESENTATIONS+ *+ * Option values usually came from syntax parser in form of defList object,+ * 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.+ *+ * Value List+ *+ * Value List is an internal representation that is used while converting+ * option values between different representation. Value List item is called+ * "parsed", when Value's value is converted to a proper data type and+ * validated, or is called "unparsed", when Value's value is stored as raw+ * string that was obtained from the source without any checks. In conversion+ * function names first case is referred as Values, second case is referred as+ * RawValues. Value List is implemented as List of option_value C-structures.+ *+ * defList+ *+ * Options in form of definition List that comes from syntax parser. (For+ * reloptions it is a part of SQL query that goes after WITH, SET or RESET+ * keywords). Can be converted to Value List using optionsDefListToRawValues+ * and can be obtained from TEXT[] via optionsTextArrayToDefList 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 Value List 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+ * representation can be obtained from Vale List using optionsValuesToBytea+ * function, and can't be converted back.+ */++/*+ * OPTION STRING VALUE NOTION+ *+ * Important thing for bytea representation is that all data should be stored+ * in one bytea chunk, including values of the string options. This is needed+ * as * bytea representation is cached, and can freed, moved or recreated again+ * without any notion, so it can't have parts allocated separately.+ *+ * Thus memory chunk for Bytea option values representation is divided into two+ * parts. First goes a C-structure that stores fixed length vales. Then goes+ * memory area reserved for string values.+ *+ * For string values C-structure stores offsets (not pointers). These offsets+ * can be used to access attached string value:+ *+ * String_pointer = Bytea_head_pointer + offest.+ */++static option_spec_basic *allocateOptionSpec(int type, const char *name,+ const char *desc, LOCKMODE lockmode,+ int struct_offset, bool is_local,+ option_value_postvalidate postvalidate_fn);++static void parse_one_option(option_value * option, bool validate);+static void *optionsAllocateBytea(options_spec_set * spec_set, List *options);+++static List * optionsDefListToRawValues(List *defList, bool is_for_reset);+static Datum optionsValuesToTextArray(List *options_values);+static List *optionsMergeOptionValues(List *old_options, List *new_options);++/*+ * 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 representation+ *+ * 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 bytea_size, bool is_local,+ int num_items_expected)+{+ MemoryContext oldcxt;+ options_spec_set *spec_set;++ if (! is_local)+ 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 = bytea_size;+ spec_set->postprocess_fun = NULL;+ spec_set->is_local = is_local;+ if (! is_local)+ 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, int struct_offset, bool is_local,+ option_value_postvalidate postvalidate_fn)+{+ MemoryContext oldcxt;+ size_t size;+ option_spec_basic *newoption;++ if (! is_local)+ 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->struct_offset = struct_offset;+ newoption->postvalidate_fn = postvalidate_fn;++ if (! is_local)+ 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 = NULL;++ Assert(!spec_set->forbid_realloc);+ if (!spec_set->is_local)+ 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 *));+ }+ if (!spec_set->is_local)+ 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, int struct_offset,+ option_value_postvalidate postvalidate_fn,+ bool default_val)+{+ option_spec_bool *spec_set_item;++ spec_set_item = (option_spec_bool *) allocateOptionSpec(OPTION_TYPE_BOOL,+ name, desc, lockmode, struct_offset,+ spec_set->is_local, postvalidate_fn);++ 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, int struct_offset,+ option_value_postvalidate postvalidate_fn,+ 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, struct_offset,+ spec_set->is_local, postvalidate_fn);++ 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, int struct_offset,+ option_value_postvalidate postvalidate_fn,+ 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, struct_offset,+ spec_set->is_local, postvalidate_fn);++ 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, int struct_offset,+ option_value_postvalidate postvalidate_fn,+ 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, struct_offset,+ spec_set->is_local, postvalidate_fn);++ 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, int struct_offset,+ option_value_postvalidate postvalidate_fn,+ const char *default_val, validate_string_option validator,+ fill_string_option filler)+{+ 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, struct_offset,+ spec_set->is_local, postvalidate_fn);+ spec_set_item->validate_cb = validator;+ spec_set_item->fill_cb = filler;++ 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);+}++/* optionsDefListToRawValues+ * Converts options values from DefList representation into Raw Values+ * List.+ *+ * No parsing is done here except for checking that RESET syntax is correct+ * (i.e. does not have =name part of value=name template). Syntax analyzer does+ * not see difference between SET and RESET cases, so we should treat it here+ * manually+ */+static List *+optionsDefListToRawValues(List *defList, bool is_for_reset)+{+ 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 (is_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 options Values List (option_values) into TEXT[] representation+ *+ * This conversion is usually needed for saving option values into database+ * (e.g. to store reloptions in pg_class.reloptions)+ */++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 option values from TEXT[] representation (datum_array) into+ * Raw Values List.+ *+ * Used while fetching options values from DB, as a first step of converting+ * them to other representations.+ */+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+ * Updates(or Resets) values from one Options Values List(old_options),+ * with values from another Options Values List (new_options)++ * This function is used while ALTERing options of some object.+ * If option from new_options list has OPTION_VALUE_STATUS_FOR_RESET flag+ * on, option with that name will be excluded result list.+ */+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+ * Checks that defList has only options with namespaces from+ * allowed_namespaces array. Items without namespace are also accepted+ *+ * Used while validation of syntax parser output. Error is thrown if unproper+ * namespace is found.+ */+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+ * Filter out DefList items that has "namespace" namespace. If "namespace"+ * is NULL, only namespaseless options are returned+ */+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+ * Converts option values from TEXT[] representation into DefList+ * representation.+ */+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;+}++/*+ * optionsDefListToTextArray+ * Converts option values from DefList representation into TEXT[]+ * representation.+ */+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)+ {+ /*+ * This function is used for backward compatibility in the place+ * where namespases are not allowed+ */+ Assert(false); /* Should not get here */+ 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+ * Transforms RawValues List into [Parsed] Values List. Validation is done+ * if validate flag is set.+ *+ * Options data that come parsed SQL query or DB storage, first converted into+ * RawValues (where value are kept in text format), then is parsed into+ * Values using this function+ *+ * Validation is used only for data that came from SQL query. We trust that+ * data that came from DB is correct.+ *+ * If validation is off, all unknown options are kept unprsed so they will+ * be stored back to DB until user RESETs them directly.+ *+ * This function destroys incoming list.+ */+List *+optionsParseRawValues(List *raw_values, options_spec_set * spec_set,+ bool validate)+{+ ListCell *cell;+ List *result = NIL;+ bool *is_set;+ int i;++ is_set = palloc0(sizeof(bool) * spec_set->num);+ foreach(cell, raw_values)+ {+ option_value *option = (option_value *) lfirst(cell);+ bool found = false;++ /* option valuess with RESET status does not need parsing */+ Assert(option->status != OPTION_VALUE_STATUS_FOR_RESET);++ /* Should not parse Values Set twice*/+ Assert(option->status != OPTION_VALUE_STATUS_PARSED);++ 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 *opt_spec = spec_set->definitions[i];++ if (strcmp(option->raw_name, opt_spec->name) == 0)+ {+ if (validate && is_set[i])+ {+ ereport(ERROR,+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),+ errmsg("parameter \"%s\" specified more than once",+ option->raw_name)));+ }+ pfree(option->raw_name);+ option->raw_name = NULL;+ option->gen = opt_spec;+ parse_one_option(option, validate);+ is_set[i] = true;+ found = true;+ break;+ }+ }+ if (validate && !found)+ {+ 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)));+ }+ result = lappend(result, option);+ }+ return result;+}++/*+ * parse_one_option+ * Function to parse and validate single option.+ *+ * See optionsParseRawValues for more info.+ *+ * Link to Option Spec for the option is embedded into "option_value"+ */+static void+parse_one_option(option_value * option, 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 (validate && option->gen->postvalidate_fn)+ {+ option->gen->postvalidate_fn(option);+ }++ if (parsed)+ option->status = OPTION_VALUE_STATUS_PARSED;++}++/*+ * optionsAllocateBytea+ * Allocates memory for Bytea options representation+ *+ * We need special function for this, as string option values are embedded into+ * Bytea object, stored at the rear part of the memory chunk. Thus we need to+ * allocate extra memory, so all string values would fit in. This function+ * calculates required size and do allocation.+ *+ * See "OPTION STRING VALUE NOTION" at the beginning of the file for better+ * understanding.+ */+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 *opt_spec = spec_set->definitions[i];+ option_spec_string *opt_spec_str;+ bool found = false;+ option_value *option;+ const char *val = NULL;++ /* Not interested in non-string options, skipping */+ if (opt_spec->type != OPTION_TYPE_STRING)+ continue;+ /*+ * Trying to find option_value that references opt_spec+ * entry+ */+ opt_spec_str = (option_spec_string *) opt_spec;+ foreach(cell, options)+ {+ option = (option_value *) lfirst(cell);+ if (option->status == OPTION_VALUE_STATUS_PARSED &&+ strcmp(option->gen->name, opt_spec->name) == 0)+ {+ found = true;+ break;+ }+ }+ if (found)+ val = option->values.string_val;+ else+ val = opt_spec_str->default_val;++ if (opt_spec_str->fill_cb)+ length = opt_spec_str->fill_cb(val, NULL);+ else+ if (val)+ length = strlen(val) + 1;+ else+ length = 0; /* "Default Value is NULL" case */++ /* Add total length of each string values to basic size */+ size += length;+ }++ res = palloc0(size);+ SET_VARSIZE(res, size);+ return res;+}++/*+ * optionsValuesToBytea+ * Converts options values from Value List representation to Bytea+ * representation.+ *+ * Fills resulting Bytea with option values according to Option Sec Set.+ *+ * For understanding processing of the string option please read "OPTION STRING+ * VALUE NOTION" at the beginning of the file.+ */+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 *opt_spec = spec_set->definitions[i];++ if (opt_spec->struct_offset < 0)+ continue; /* This option value should not be stored in Bytea for+ * some reason. May be it is deprecated and has warning+ * or error in postvalidate function */++ /* Calculate the position of the item inside the structure */+ item_pos = data + opt_spec->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(opt_spec->name, option->gen->name) == 0)+ {+ found = option;+ break;+ }+ }+ /* writing to the proper position either option value or default val */+ switch (opt_spec->type)+ {+ case OPTION_TYPE_BOOL:+ *(bool *) item_pos = found ?+ found->values.bool_val :+ ((option_spec_bool *) opt_spec)->default_val;+ break;+ case OPTION_TYPE_INT:+ *(int *) item_pos = found ?+ found->values.int_val :+ ((option_spec_int *) opt_spec)->default_val;+ break;+ case OPTION_TYPE_REAL:+ *(double *) item_pos = found ?+ found->values.real_val :+ ((option_spec_real *) opt_spec)->default_val;+ break;+ case OPTION_TYPE_ENUM:+ *(int *) item_pos = found ?+ found->values.enum_val :+ ((option_spec_enum *) opt_spec)->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;+ option_spec_string * opt_spec_str =+ (option_spec_string *) opt_spec;++ if (found)+ value = found->values.string_val;+ else+ value = opt_spec_str->default_val;++ if (opt_spec_str->fill_cb)+ {+ Size size =+ opt_spec_str->fill_cb(value, string_values_buffer);+ if (size)+ {+ *(int *) item_pos = string_values_buffer - data;+ string_values_buffer += size;+ } else+ *(int *) item_pos =+ OPTION_STRING_VALUE_NOT_SET_OFFSET;+ } else+ {+ *(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",+ opt_spec->type);+ break;+ }+ }+ return (void *) data;+}++/*+ * optionDefListToTextArray+ * Converts options from defList to TEXT[] representation.+ *+ * Used when new relation (or other object) is created. defList comes from+ * SQL syntax parser, TEXT[] goes to DB storage. Options are always validated+ * while coversion.+ */+Datum+optionDefListToTextArray(options_spec_set * spec_set, List *defList)+{+ Datum result;+ List *new_values;++ /* Parse and validate new values */+ new_values = optionsDefListToRawValues(defList, false);+ new_values = optionsParseRawValues(new_values, spec_set, true);++ /* Some checks can be done in postprocess_fun, we should call it */+ if (spec_set->postprocess_fun)+ {+ bytea *data = optionsValuesToBytea(new_values, spec_set);++ spec_set->postprocess_fun(data, true);+ pfree(data);+ }+ result = optionsValuesToTextArray(new_values);+ return result;+}++/*+ * optionsUpdateTexArrayWithDefList+ * Modifies oldOptions values (in TEXT[] representation) with defList+ * values.+ *+ * Old values are appened or replaced with new values if do_reset flag is set+ * to false. If do_reset is set to true, defList specify the list of the options+ * that should be removed from original list.+ */++Datum+optionsUpdateTexArrayWithDefList(options_spec_set * spec_set, Datum oldOptions,+ List *defList, bool do_reset)+{+ Datum result;+ List *new_values;+ List *old_values;+ List *merged_values;++ /*+ * Parse and validate New values+ */+ new_values = optionsDefListToRawValues(defList, do_reset);+ if (! do_reset)+ new_values = optionsParseRawValues(new_values, spec_set, true);++ if (PointerIsValid(DatumGetPointer(oldOptions)))+ {+ old_values = optionsTextArrayToRawValues(oldOptions);+ merged_values = optionsMergeOptionValues(old_values, new_values);+ }+ else+ {+ if (do_reset)+ merged_values = NULL; /* return nothing */+ 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+ * Convert options values from TEXT[] representation into Bytea+ * representation+ *+ * This function uses other conversion function to get desired result.+ */+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 = 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.cindex 003663ab81..26d695d9b1 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.cindex 599e160ca6..760e532a66 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-2022, 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,389 +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- },- {- {- "security_invoker",- "Privileges on underlying relations are checked as the invoking user, not the view owner",- 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},
@@ -489,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},
@@ -507,225 +115,9 @@ 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);-/*- * Get the length of a string reloption (either default or the user-defined- * value). This is used for allocation purposes when building a set of- * relation options.- */-#define GET_STRING_RELOPTION_LEN(option) \- ((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;-}+options_spec_set *get_stdrd_relopt_spec_set(bool is_for_toast);+void oid_postvalidate(option_value* value);
/*
* init_local_reloptions
@@ -735,9 +127,8 @@ add_reloption(relopt_gen *newoption)
void
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, true, 0);
}
/*
@@ -751,112 +142,6 @@ register_reloptions_validator(local_relopts *opts, relopts_validator validator)
opts->validators = lappend(opts->validators, 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
@@ -867,47 +152,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, offset, NULL,+ default_val);
}
/*
@@ -917,454 +163,60 @@ add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_
* 'offset' is offset of int-typed field.
*/
void
-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);-}--/*- * add_local_real_reloption- * Add a new local float reloption- *- * 'offset' is offset of double-typed field.- */-void-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);-- add_reloption((relopt_gen *) newoption);-}--/*- * add_local_enum_reloption- * Add a new local enum reloption- *- * 'offset' is offset of int-typed field.- */-void-add_local_enum_reloption(local_relopts *relopts, const char *name,- const char *desc, relopt_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);-}--/*- * add_local_string_reloption- * Add a new local string reloption- *- * 'offset' is offset of int-typed field that will store offset of string value- * in the resulting bytea structure.- */-void-add_local_string_reloption(local_relopts *relopts, const char *name,- const char *desc, const char *default_val,- 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;+add_local_int_reloption(local_relopts *relopts, const char *name,+ const char *desc, int default_val, int min_val,+ int max_val, int offset)+{+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,+ default_val, min_val, max_val);
}
-
/*
- * Convert the text-array format of reloptions into a List of DefElem.- * This is the inverse of transformRelOptions().+ * add_local_real_reloption+ * Add a new local float reloption+ *+ * 'offset' is offset of double-typed field.
*/
-List *-untransformRelOptions(Datum options)+void+add_local_real_reloption(local_relopts *relopts, const char *name,+ const char *desc, double default_val,+ double min_val, double max_val, int offset)
{
- 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);+ optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, offset, NULL,+ default_val, min_val, max_val);- 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));- }+/*+ * add_local_enum_reloption+ * Add a new local enum reloption+ *+ * 'offset' is offset of int-typed field.+ */+void+add_local_enum_reloption(local_relopts *relopts, const char *name,+ const char *desc, opt_enum_elt_def *members,+ int default_val, const char *detailmsg, int offset)+{+ optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, offset, NULL,+ members, default_val, detailmsg);+}- return result;+/*+ * add_local_string_reloption+ * Add a new local string reloption+ *+ * 'offset' is offset of int-typed field that will store offset of string value+ * in the resulting bytea structure.+ */+void+add_local_string_reloption(local_relopts *relopts, const char *name,+ const char *desc, const char *default_val,+ validate_string_relopt validator,+ fill_string_relopt filler, int offset)+{+ optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, offset,+ NULL, default_val, validator, filler);
}
/*
@@ -1381,12 +233,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,
@@ -1400,708 +253,478 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
/* Parse into appropriate format; don't error out here */
switch (classForm->relkind)
{
- case RELKIND_RELATION:
case RELKIND_TOASTVALUE:
+ spec_set = get_toast_relopt_spec_set();+ break;+ case RELKIND_RELATION:
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)+void+oid_postvalidate(option_value* value)
{
- ArrayType *array = DatumGetArrayTypeP(options);- Datum *optiondatums;- int noptions;- int i;-- deconstruct_array(array, TEXTOID, -1, false, TYPALIGN_INT,- &optiondatums, NULL, &noptions);-- 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);-- if (((void *) array) != DatumGetPointer(options))- pfree(array);+ if (value->values.bool_val)+ ereport(ERROR,+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),+ errmsg("tables declared WITH OIDS are not supported")));
}
/*
- * 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.+ * These toast options are can't be set via SQL, but we should set them+ * to their defaults in binary representation, to make postgres work properly
*/
-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++;- }- }+static void+toast_options_postprocess(void *data, bool validate)+{+ StdRdOptions * toast_options = (StdRdOptions *) data;+ toast_options->fillfactor = 100;+ toast_options->autovacuum.analyze_threshold = -1;+ toast_options->autovacuum.analyze_scale_factor = -1;+}+++options_spec_set *+get_stdrd_relopt_spec_set(bool is_heap)+{+ options_spec_set * stdrd_relopt_spec_set = allocateOptionsSpecSet(+ is_heap ? NULL : "toast", sizeof(StdRdOptions), false, 0);+ if (!is_heap)+ stdrd_relopt_spec_set->postprocess_fun = toast_options_postprocess;++ if (is_heap)+ optionsSpecSetAddInt(stdrd_relopt_spec_set, "fillfactor",+ "Packs table pages only to this percentag",+ ShareUpdateExclusiveLock, /* since it applies only+ * to later inserts */+ offsetof(StdRdOptions, fillfactor), NULL,+ HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100);++ optionsSpecSetAddBool(stdrd_relopt_spec_set, "autovacuum_enabled",+ "Enables autovacuum in this relation",+ ShareUpdateExclusiveLock,+ offsetof(StdRdOptions, autovacuum) ++ offsetof(AutoVacOpts, enabled),+ NULL, true);++ optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_vacuum_threshold",+ "Minimum number of tuple updates or deletes prior to vacuum",+ ShareUpdateExclusiveLock,+ offsetof(StdRdOptions, autovacuum) ++ offsetof(AutoVacOpts, vacuum_threshold),+ NULL, -1, 0, INT_MAX);++ if (is_heap)+ optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_analyze_threshold",+ "Minimum number of tuple updates or deletes prior to vacuum",+ ShareUpdateExclusiveLock,+ offsetof(StdRdOptions, autovacuum) ++ offsetof(AutoVacOpts, analyze_threshold),+ NULL, -1, 0, INT_MAX);++ optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_vacuum_cost_limit",+ "Vacuum cost amount available before napping, for autovacuum",+ ShareUpdateExclusiveLock,+ offsetof(StdRdOptions, autovacuum) ++ offsetof(AutoVacOpts, vacuum_cost_limit),+ NULL, -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,+ offsetof(StdRdOptions, autovacuum) ++ offsetof(AutoVacOpts, freeze_min_age),+ NULL, -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,+ offsetof(StdRdOptions, autovacuum) ++ offsetof(AutoVacOpts, freeze_max_age),+ NULL, -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,+ offsetof(StdRdOptions, autovacuum) ++ offsetof(AutoVacOpts, freeze_table_age),+ NULL, -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,+ offsetof(StdRdOptions, autovacuum) ++ offsetof(AutoVacOpts, multixact_freeze_min_age),+ NULL, -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,+ offsetof(StdRdOptions, autovacuum) ++ offsetof(AutoVacOpts, multixact_freeze_max_age),+ NULL, -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,+ offsetof(StdRdOptions, autovacuum) ++ offsetof(AutoVacOpts, multixact_freeze_table_age),+ NULL, -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,+ offsetof(StdRdOptions, autovacuum) ++ offsetof(AutoVacOpts, log_min_duration),+ NULL, -1, -1, INT_MAX);++ optionsSpecSetAddReal(stdrd_relopt_spec_set, "autovacuum_vacuum_cost_delay",+ "Vacuum cost delay in milliseconds, for autovacuum",+ ShareUpdateExclusiveLock,+ offsetof(StdRdOptions, autovacuum) ++ offsetof(AutoVacOpts, vacuum_cost_delay),+ NULL, -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,+ offsetof(StdRdOptions, autovacuum) ++ offsetof(AutoVacOpts, vacuum_scale_factor),+ NULL, -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,+ offsetof(StdRdOptions, autovacuum) ++ offsetof(AutoVacOpts, vacuum_ins_scale_factor),+ NULL, -1, 0.0, 100.0);+ if (is_heap)+ {+ 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,+ offsetof(StdRdOptions, autovacuum) ++ offsetof(AutoVacOpts, analyze_scale_factor),+ NULL, -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,+ offsetof(StdRdOptions, toast_tuple_target),+ NULL, 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,+ offsetof(StdRdOptions, user_catalog_table),+ NULL, false);++ optionsSpecSetAddInt(stdrd_relopt_spec_set, "parallel_workers",+ "Number of parallel processes that can be used per executor node for this relation.",+ ShareUpdateExclusiveLock,+ offsetof(StdRdOptions, parallel_workers),+ NULL, -1, 0, 1024);
}
- /* Done if no options */- if (PointerIsValid(DatumGetPointer(options)))- parseRelOptionsInternal(options, validate, reloptions, numoptions);+ optionsSpecSetAddEnum(stdrd_relopt_spec_set, "vacuum_index_cleanup",+ "Controls index vacuuming and index cleanup",+ ShareUpdateExclusiveLock,+ offsetof(StdRdOptions, vacuum_index_cleanup),+ NULL, 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,+ offsetof(StdRdOptions, vacuum_truncate),+ NULL, true);++ if (is_heap)+ {+ optionsSpecSetAddBool(stdrd_relopt_spec_set, "oids",+ "Backward compatibility option. Will do nothing when false, will throw error when true",+ NoLock,+ -1, /* Do not actually store it's value */+ &oid_postvalidate, false);+ }- *numrelopts = numoptions;- return reloptions;+ return stdrd_relopt_spec_set;
}
-/* Parse local unregistered options. */-static relopt_value *-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)++static options_spec_set *heap_relopt_spec_set = NULL;++options_spec_set *+get_heap_relopt_spec_set(void)
{
- int nopts = list_length(relopts->options);- relopt_value *values = palloc(sizeof(*values) * nopts);- ListCell *lc;- int i = 0;+ if (heap_relopt_spec_set)+ return heap_relopt_spec_set;+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);+ return heap_relopt_spec_set;+}- foreach(lc, relopts->options)- {- local_relopt *opt = lfirst(lc);+static options_spec_set *toast_relopt_spec_set = NULL;- values[i].gen = opt->option;- values[i].isset = false;+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(false);+ return toast_relopt_spec_set;+}- i++;- }+static options_spec_set *partitioned_relopt_spec_set = NULL;- if (options != (Datum) 0)- parseRelOptionsInternal(options, validate, values, nopts);+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), false, 0);+ /* No options for now, so spec set is empty */- return values;+ return partitioned_relopt_spec_set;
}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's- * value+ * 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 void-parse_one_reloption(relopt_value *option, char *text_str, int text_len,- bool validate)+void *+build_local_reloptions(local_relopts *relopts, Datum options, bool validate)
{
- char *value;- int value_len;- bool parsed;- bool nofree = false;+ void *opts;+ ListCell *lc;+ List *values;- if (option->isset && validate)- ereport(ERROR,- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),- errmsg("parameter \"%s\" specified more than once",- option->gen->name)));+ values = optionsTextArrayToRawValues(options);+ values = optionsParseRawValues(values, relopts->spec_set, validate);+ opts = optionsValuesToBytea(values, relopts->spec_set);- 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';+ /*+ * Kind of ugly conversion here for backward compatibility.+ * Would be removed while moving opclass options to options.c API+ */- switch (option->gen->type)+ if (validate && relopts->validators)
{
- 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;- }-- 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).- */-static void *-allocateReloptStruct(Size base, relopt_value *options, int numoptions)-{- Size size = base;- int i;+ int val_count = 0;+ int i;+ option_value *val_array;- for (i = 0; i < numoptions; i++)- {- relopt_value *optval = &options[i];+ foreach(lc, values)+ val_count++;+ val_array = palloc(sizeof(option_value)*val_count);- if (optval->gen->type == RELOPT_TYPE_STRING)+ i=0;+ foreach(lc, values)
{
- 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;+ option_value *val = lfirst(lc);+ memcpy(&(val_array[i]), val, sizeof(option_value));+ i++;
}
- }-- return palloc0(size);-}--/*- * 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)-{- int i;- int offset = basesize;- for (i = 0; i < numoptions; i++)- {- int j;- bool found = false;+ foreach(lc, relopts->validators)+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);- 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);+ pfree(val_array);
}
- SET_VARSIZE(rdopts, offset);-}-+ return opts;-/*- * 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));
}
/*
- * 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_view_relopt_spec_set+ * Returns an options catalog for view relation.
*/
-void *-build_reloptions(Datum reloptions, bool validate,- relopt_kind kind,- Size relopt_struct_size,- const relopt_parse_elt *relopt_elems,- int num_relopt_elems)+static options_spec_set *view_relopt_spec_set = NULL;++options_spec_set *+get_view_relopt_spec_set(void)
{
- int numoptions;- relopt_value *options;- void *rdopts;+ if (view_relopt_spec_set)+ return view_relopt_spec_set;- /* parse options specific to given relation option kind */- options = parseRelOptions(reloptions, validate, kind, &numoptions);- Assert(numoptions <= num_relopt_elems);+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,+ sizeof(ViewOptions), false, 3);- /* if none set, we're done */- if (numoptions == 0)- {- Assert(options == NULL);- return NULL;- }+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",+ "View acts as a row security barrier",+ AccessExclusiveLock,+ offsetof(ViewOptions, security_barrier), NULL, false);- /* 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);+ optionsSpecSetAddBool(view_relopt_spec_set, "security_invoker",+ "Privileges on underlying relations are checked as the invoking user, not the view owner",+ AccessExclusiveLock,+ offsetof(ViewOptions, security_invoker), NULL, false);- pfree(options);+ optionsSpecSetAddEnum(view_relopt_spec_set, "check_option",+ "View has WITH CHECK OPTION defined (local or cascaded)",+ AccessExclusiveLock,+ offsetof(ViewOptions, check_option), NULL,+ viewCheckOptValues,+ VIEW_OPTION_CHECK_OPTION_NOT_SET,+ gettext_noop("Valid values are \"local\" and \"cascaded\"."));- return rdopts;+ return view_relopt_spec_set;
}
/*
- * 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.+ * get_attribute_options_spec_set+ * Returns an options spec set for heap attributes
*/
-void *-build_local_reloptions(local_relopts *relopts, Datum options, bool validate)-{- int noptions = list_length(relopts->options);- relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);- relopt_value *vals;- void *opts;- int i = 0;- ListCell *lc;-- 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;+static options_spec_set *attribute_options_spec_set = NULL;- i++;- }+options_spec_set *+get_attribute_options_spec_set(void)+{+ if (attribute_options_spec_set)+ return attribute_options_spec_set;- vals = parseLocalRelOptions(relopts, options, validate);- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,- elems, noptions);+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,+ sizeof(AttributeOpts), false, 2);- foreach(lc, relopts->validators)- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);+ optionsSpecSetAddReal(attribute_options_spec_set, "n_distinct",+ "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",+ ShareUpdateExclusiveLock,+ offsetof(AttributeOpts, n_distinct), NULL, 0, -1.0, DBL_MAX);- if (elems)- pfree(elems);+ optionsSpecSetAddReal(attribute_options_spec_set,+ "n_distinct_inherited",+ "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",+ ShareUpdateExclusiveLock,+ offsetof(AttributeOpts, n_distinct_inherited), NULL, 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)},- {"security_invoker", RELOPT_TYPE_BOOL,- offsetof(ViewOptions, security_invoker)},- {"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;- }-}+ tablespace_options_spec_set = allocateOptionsSpecSet(NULL,+ sizeof(TableSpaceOpts), false, 4);+ optionsSpecSetAddReal(tablespace_options_spec_set,+ "random_page_cost",+ "Sets the planner's estimate of the cost of a nonsequentially fetched disk page",+ ShareUpdateExclusiveLock,+ offsetof(TableSpaceOpts, random_page_cost),+ NULL, -1, 0.0, DBL_MAX);-/*- * 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);-- /* Assume function is strict */- if (!PointerIsValid(DatumGetPointer(reloptions)))- return NULL;+ optionsSpecSetAddReal(tablespace_options_spec_set, "seq_page_cost",+ "Sets the planner's estimate of the cost of a sequentially fetched disk page",+ ShareUpdateExclusiveLock,+ offsetof(TableSpaceOpts, seq_page_cost),+ NULL, -1, 0.0, DBL_MAX);- return amoptions(reloptions, validate);+ optionsSpecSetAddInt(tablespace_options_spec_set, "effective_io_concurrency",+ "Number of simultaneous requests that can be handled efficiently by the disk subsystem",+ ShareUpdateExclusiveLock,+ offsetof(TableSpaceOpts, effective_io_concurrency),+ NULL,+#ifdef USE_PREFETCH+ -1, 0, MAX_IO_CONCURRENCY+#else+ 0, 0, 0+#endif+ );++ optionsSpecSetAddInt(tablespace_options_spec_set,+ "maintenance_io_concurrency",+ "Number of simultaneous requests that can be handled efficiently by the disk subsystem for maintenance work.",+ ShareUpdateExclusiveLock,+ offsetof(TableSpaceOpts, maintenance_io_concurrency),+ NULL,+#ifdef USE_PREFETCH+ -1, 0, MAX_IO_CONCURRENCY+#else+ 0, 0, 0+#endif+ );+ }+ return tablespace_options_spec_set;
}
/*
- * Option parser for attribute reloptions+ * Relation 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.
*/
-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));-}-/*- * 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));-}
/*
* Determine the required LOCKMODE from an option list.
@@ -2110,33 +733,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;
}
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.cindex 3d15701a01..a6117665d1 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"
/*
@@ -68,7 +69,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;
@@ -83,6 +83,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -605,21 +606,6 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
return entries;
}
-bytea *-ginoptions(Datum reloptions, bool validate)-{- static const relopt_parse_elt tab[] = {- {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)},- {"gin_pending_list_limit", RELOPT_TYPE_INT, offsetof(GinOptions,- pendingListCleanupSize)}- };-- return (bytea *) build_reloptions(reloptions, validate,- RELOPT_KIND_GIN,- sizeof(GinOptions),- tab, lengthof(tab));-}-
/*
* Fetch index's statistical data into *stats
*
@@ -706,3 +692,29 @@ 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), false, 2);++ optionsSpecSetAddBool(gin_relopt_specset, "fastupdate",+ "Enables \"fast update\" feature for this GIN index",+ AccessExclusiveLock,+ offsetof(GinOptions, useFastUpdate), NULL,+ 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,+ offsetof(GinOptions, pendingListCleanupSize), NULL,+ -1, 64, MAX_KILOBYTES);++ return gin_relopt_specset;+}diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.cindex 8c6c744ab7..f7300f5c23 100644--- a/src/backend/access/gist/gist.c+++ b/src/backend/access/gist/gist.c@@ -90,7 +90,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;
@@ -105,6 +104,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/gistutil.c b/src/backend/access/gist/gistutil.cindex d4bf0c7563..78c3f8976e 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 "common/pg_prng.h"
#include "storage/indexfsm.h"
@@ -917,20 +917,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.
*
@@ -1065,3 +1051,39 @@ 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), false, 2);++ optionsSpecSetAddInt(gist_relopt_specset, "fillfactor",+ "Packs gist index pages only to this percentage",+ NoLock, /* No ALTER, no lock */+ offsetof(GiSTOptions, fillfactor), NULL,+ GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100);++ optionsSpecSetAddEnum(gist_relopt_specset, "buffering",+ "Enables buffering build for this GiST index",+ NoLock, /* No ALTER, no lock */+ offsetof(GiSTOptions, buffering_mode), NULL,+ 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.cindex a259a301fa..31c9c77941 100644--- a/src/backend/access/hash/hash.c+++ b/src/backend/access/hash/hash.c@@ -87,7 +87,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;
@@ -102,6 +101,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/hashutil.c b/src/backend/access/hash/hashutil.cindex edb6fa968f..a62fbcba93 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,22 @@ _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), false, 1);+ optionsSpecSetAddInt(hash_relopt_specset, "fillfactor",+ "Packs hash index pages only to this percentage",+ NoLock, /* No ALTER -- no lock */+ offsetof(HashOptions, fillfactor), NULL,+ HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100);++ return hash_relopt_specset;+}diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.cindex c9b4964c1e..9930172037 100644--- a/src/backend/access/nbtree/nbtree.c+++ b/src/backend/access/nbtree/nbtree.c@@ -23,6 +23,7 @@
#include "access/relscan.h"
#include "access/xlog.h"
#include "access/xloginsert.h"
+#include "access/options.h"
#include "commands/progress.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
@@ -126,7 +127,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;
@@ -141,6 +141,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1420,3 +1421,35 @@ 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), false, 3);++ optionsSpecSetAddInt(bt_relopt_specset, "fillfactor",+ "Packs btree index pages only to this percentage",+ ShareUpdateExclusiveLock, /* affects inserts only */+ offsetof(BTOptions, fillfactor), NULL,+ 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,+ offsetof(BTOptions,vacuum_cleanup_index_scale_factor),+ NULL, -1, 0.0, 1e10);++ optionsSpecSetAddBool(bt_relopt_specset, "deduplicate_items",+ "Enables \"deduplicate items\" feature for this btree index",+ ShareUpdateExclusiveLock, /* affects inserts only */+ offsetof(BTOptions,deduplicate_items), NULL,+ true);++ return bt_relopt_specset;+}diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.cindex 84164748b3..080e25e9fa 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"
@@ -2107,25 +2107,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.cindex 1ae7492216..1cec98c3a9 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"
@@ -74,7 +74,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;
@@ -89,6 +88,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -733,23 +733,6 @@ SpGistInitMetapage(Page page)
((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) 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).
@@ -1349,3 +1332,24 @@ spgproperty(Oid index_oid, int attno,
return true;
}
++static options_spec_set *spgist_relopt_specset = NULL;++void *+spggetreloptspecset(void)+{++ if (spgist_relopt_specset)+ return spgist_relopt_specset;++ spgist_relopt_specset = allocateOptionsSpecSet(NULL,+ sizeof(SpGistOptions), false, 1);++ optionsSpecSetAddInt(spgist_relopt_specset, "fillfactor",+ "Packs spgist index pages only to this percentage",+ ShareUpdateExclusiveLock, /* affects only inserts */+ offsetof(SpGistOptions, fillfactor), NULL,+ SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100);++ return spgist_relopt_specset;+}diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.cindex 9abbb6b555..53885088ab 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 = optionDefListToTextArray(get_toast_relopt_spec_set(),+ toastDefList);
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.cindex ea27857bb8..13e50cd08e 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.cindex cd30f15eba..1a41bf0650 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,11 +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 (stmt->options && ! amreloptspecsetfn)+ {+ ereport(ERROR,+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),+ errmsg("access method %s does not support options",+ accessMethodName)));+ }+ if (amreloptspecsetfn)+ reloptions = optionDefListToTextArray(amreloptspecsetfn(),+ stmt->options);+ else+ reloptions = (Datum) 0;
/*
* Prepare arguments for index_create, primarily an IndexInfo structure.
* Note that predicates must be in implicit-AND format. In a concurrent
@@ -1987,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.cindex 124b9961dc..7c2b97426f 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"
@@ -672,7 +673,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;
@@ -820,19 +820,36 @@ 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 = optionDefListToTextArray(get_view_relopt_spec_set(),+ stmt->options);
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 = optionDefListToTextArray(+ get_partitioned_relopt_spec_set(), heapDefList);
break;
+ }
default:
- (void) heap_reloptions(relkind, reloptions, true);+ {+ /* If it is not all listed above, is should be heap */+ char *namespaces[] = HEAP_RELOPT_NAMESPACES;+ List *heapDefList;++ optionsDefListValdateNamespaces(stmt->options, namespaces);+ heapDefList = optionsDefListFilterNamespaces(stmt->options, NULL);+ reloptions = optionDefListToTextArray(get_heap_relopt_spec_set(),+ heapDefList);+ }
}
if (stmt->ofTypename)
@@ -4104,7 +4121,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);
@@ -4146,7 +4163,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.
@@ -4367,9 +4384,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;
@@ -8132,12 +8153,13 @@ 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);+ if (isnull)+ datum = (Datum) 0;+ newOptions = optionsUpdateTexArrayWithDefList(+ get_attribute_options_spec_set(),+ datum, castNode(List, options),+ isReset);
/* Build new tuple. */
memset(repl_null, false, sizeof(repl_null));
memset(repl_repl, false, sizeof(repl_repl));
@@ -14116,12 +14138,13 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
HeapTuple tuple;
HeapTuple newtuple;
Datum datum;
- bool isnull;
Datum newOptions;
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 *optionsDefList;+ options_spec_set *optionsSpecSet;+ char *heap_namespaces[] = HEAP_RELOPT_NAMESPACES;
if (defList == NIL && operation != AT_ReplaceRelOptions)
return; /* nothing to do */
@@ -14141,38 +14164,58 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
* there were none before.
*/
datum = (Datum) 0;
- isnull = true;
}
else
{
+ bool isnull;
/* Get the old reloptions */
datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
&isnull);
+ if (isnull)+ datum = (Datum) 0;
}
/* Generate new proposed reloptions (text array) */
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,- defList, NULL, validnsps, false,- operation == AT_ResetRelOptions);
/* Validate */
++ optionsSpecSet = NULL;
switch (rel->rd_rel->relkind)
{
case RELKIND_RELATION:
- case RELKIND_TOASTVALUE:
case RELKIND_MATVIEW:
- (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);+ optionsDefListValdateNamespaces(defList, heap_namespaces);+ optionsDefList = optionsDefListFilterNamespaces(defList, NULL);+ optionsSpecSet = get_heap_relopt_spec_set();
break;
case RELKIND_PARTITIONED_TABLE:
- (void) partitioned_table_reloptions(newOptions, true);+ optionsDefListValdateNamespaces(defList, heap_namespaces);+ optionsDefList = optionsDefListFilterNamespaces(defList, NULL);+ optionsSpecSet = get_partitioned_relopt_spec_set();
break;
case RELKIND_VIEW:
- (void) view_reloptions(newOptions, true);+ optionsDefList = defList;+ optionsSpecSet = get_view_relopt_spec_set();
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;+ }+ optionsDefList = defList;+ optionsSpecSet = rel->rd_indam->amreloptspecset();
break;
+ case RELKIND_TOASTVALUE:+ /* Should never get here */+ /* TOAST options are never altered directly */+ Assert(0);+ /* FALLTHRU */+ /* If we get here in prod. error is the best option */
default:
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -14182,11 +14225,15 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
break;
}
+ newOptions = optionsUpdateTexArrayWithDefList(optionsSpecSet, datum,+ optionsDefList,+ operation == AT_ResetRelOptions);+
/* Special-case validation of view options */
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;
@@ -14261,20 +14308,22 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
* pretend there were none before.
*/
datum = (Datum) 0;
- isnull = true;
}
else
{
+ bool isnull;
/* Get the old reloptions */
datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
&isnull);
+ if (isnull)+ datum = (Datum) 0;
}
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,- defList, "toast", validnsps, false,- operation == AT_ResetRelOptions);-- (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);+ optionsDefList = optionsDefListFilterNamespaces(defList, "toast");+ newOptions = optionsUpdateTexArrayWithDefList(+ get_toast_relopt_spec_set(),+ datum, optionsDefList,+ operation == AT_ResetRelOptions);
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.cindex 55f40831da..dea1354ad0 100644--- a/src/backend/commands/tablespace.c+++ b/src/backend/commands/tablespace.c@@ -364,10 +364,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 = optionDefListToTextArray(get_tablespace_options_spec_set(),+ stmt->options);+
if (newOptions != (Datum) 0)
values[Anum_pg_tablespace_spcoptions - 1] = newOptions;
else
@@ -1101,11 +1100,12 @@ 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);+ if (isnull)+ datum = (Datum) 0;+ newOptions = optionsUpdateTexArrayWithDefList(+ get_tablespace_options_spec_set(),+ datum, stmt->options, stmt->isReset);
/* Build new tuple. */
memset(repl_null, false, sizeof(repl_null));
memset(repl_repl, false, sizeof(repl_repl));
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.cindex cf222fc3e9..2f98df4d35 100644--- a/src/backend/foreign/foreign.c+++ b/src/backend/foreign/foreign.c@@ -79,7 +79,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
if (isnull)
fdw->options = NIL;
else
- fdw->options = untransformRelOptions(datum);+ fdw->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -166,7 +166,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
if (isnull)
server->options = NIL;
else
- server->options = untransformRelOptions(datum);+ server->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -234,7 +234,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -271,7 +271,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -304,7 +304,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -512,7 +512,7 @@ pg_options_to_table(PG_FUNCTION_ARGS)
List *options;
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- options = untransformRelOptions(array);+ options = optionsTextArrayToDefList(array);
rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
/* prepare the result set */
@@ -609,7 +609,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.cindex cd946c7692..bb1a4c4996 100644--- a/src/backend/parser/parse_utilcmd.c+++ b/src/backend/parser/parse_utilcmd.c@@ -1758,7 +1758,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;
@@ -1822,7 +1822,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.cindex 3780c6e812..063b6b4344 100644--- a/src/backend/tcop/utility.c+++ b/src/backend/tcop/utility.c@@ -1162,6 +1162,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;
@@ -1185,15 +1186,16 @@ 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 = optionDefListToTextArray(+ get_toast_relopt_spec_set(), toastDefList);
NewRelationCreateToastTable(address.objectId,
toast_options);
@@ -1302,9 +1304,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.cindex 9e252a0891..5ad32a8773 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.cindex d47fac7bb9..508af22cd7 100644--- a/src/backend/utils/cache/relcache.c+++ b/src/backend/utils/cache/relcache.c@@ -461,7 +461,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -476,11 +476,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;
@@ -491,7 +491,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.cindex 5609246c2f..95dac315f0 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.hindex a382551a98..833a5259ee 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.
*/
@@ -265,7 +264,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;
@@ -277,6 +275,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.hindex 887fb0a553..0aaf118ea5 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.hindex 2518660927..e25ed4269b 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.hindex 2935d2f353..e69fd6fd52 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.hindex 240131ef71..7e586061e7 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/options.h"
/*
* 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.hindex cd7b2a53d8..58468249de 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.hindex 9fec6fb1a8..7d3e2a600c 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.hnew file mode 100644index 0000000000..3600d41e4d--- /dev/null+++ b/src/include/access/options.h@@ -0,0 +1,261 @@+/*-------------------------------------------------------------------------+ *+ * options.h+ * An uniform, context-free API for processing name=value options. Used+ * to process relation options (reloptions), attribute options, opclass+ * options, etc.+ *+ * 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-2022, 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;++/*+ * 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;+++typedef struct option_value option_value;++/* Function that would be called after option validation.+ * Might be needed for custom warnings, errors, or for changing+ * option value after being validated, etc.+ */+typedef void (*option_value_postvalidate) (option_value* value);++/* 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_type type;+ int struct_offset; /* offset of the value in Bytea representation */+ option_value_postvalidate postvalidate_fn;+} 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; /* Null terminated array of allowed names and+ * corresponding values */+ int default_val; /* Default value, may differ from values in+ * members array */+ const char *detailmsg;+} option_spec_enum;++/* validation routines for strings */+typedef void (*validate_string_option) (const char *value);+typedef Size (*fill_string_option) (const char *value, void *ptr);++/*+ * When storing sting reloptions, we should 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 reloptions+ * 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;+ fill_string_option fill_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 */+ bool is_local; /* If true specset is in local memory context */+ 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 representation.+ * Can be used for extra+ * validation etc. */+ 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 bytea_size, bool is_local, int num_items_expected);++extern void optionsSpecSetAddBool(options_spec_set *spec_set, const char *name,+ const char *desc, LOCKMODE lockmode,+ int struct_offset,+ option_value_postvalidate postvalidate_fn,+ bool default_val);++extern void optionsSpecSetAddInt(options_spec_set *spec_set, const char *name,+ const char *desc, LOCKMODE lockmode,+ int struct_offset,+ option_value_postvalidate postvalidate_fn,+ 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,+ int struct_offset,+ option_value_postvalidate postvalidate_fn,+ 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,+ int struct_offset,+ option_value_postvalidate postvalidate_fn,+ 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, int struct_offset,+ option_value_postvalidate postvalidate_fn,+ const char *default_val,+ validate_string_option validator,+ fill_string_option filler);+++/*+ * 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 conversion, 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 optionsUpdateTexArrayWithDefList(options_spec_set * spec_set,+ Datum oldOptions, List *defList, bool do_reset);+extern Datum optionDefListToTextArray(options_spec_set * spec_set,+ List *defList);++/* Internal functions */++extern List *optionsTextArrayToRawValues(Datum array_datum);+extern List *optionsParseRawValues(List *raw_values,+ options_spec_set * spec_set, bool validate);+extern bytea *optionsValuesToBytea(List *options, options_spec_set * spec_set);+++#endif /* OPTIONS_H */diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.hindex f740513303..8514399e11 100644--- a/src/include/access/reloptions.h+++ b/src/include/access/reloptions.h@@ -1,14 +1,9 @@
/*-------------------------------------------------------------------------
*
* reloptions.h
- * Core support for relation and tablespace options (pg_class.reloptions+ * Support for relation view and tablespace options (pg_class.reloptions
* and pg_tablespace.spcoptions)
*
- * Note: the functions dealing with text-array reloptions 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-2022, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -22,178 +17,39 @@
#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"
-/* types supported by reloptions */-typedef enum relopt_type-{- RELOPT_TYPE_BOOL,- RELOPT_TYPE_INT,- RELOPT_TYPE_REAL,- RELOPT_TYPE_ENUM,- RELOPT_TYPE_STRING-} relopt_type;--/* kinds supported by reloptions */-typedef enum relopt_kind-{- RELOPT_KIND_LOCAL = 0,- RELOPT_KIND_HEAP = (1 << 0),- RELOPT_KIND_TOAST = (1 << 1),- RELOPT_KIND_BTREE = (1 << 2),- RELOPT_KIND_HASH = (1 << 3),- RELOPT_KIND_GIN = (1 << 4),- RELOPT_KIND_GIST = (1 << 5),- RELOPT_KIND_ATTRIBUTE = (1 << 6),- RELOPT_KIND_TABLESPACE = (1 << 7),- RELOPT_KIND_SPGIST = (1 << 8),- RELOPT_KIND_VIEW = (1 << 9),- RELOPT_KIND_BRIN = (1 << 10),- RELOPT_KIND_PARTITIONED = (1 << 11),- /* if you add a new kind, make sure you update "last_default" too */- RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_PARTITIONED,- /* some compilers treat enums as signed ints, so we can't use 1 << 31 */- RELOPT_KIND_MAX = (1 << 30)-} relopt_kind;-
/* reloption namespaces allowed for heaps -- currently only TOAST */
#define HEAP_RELOPT_NAMESPACES { "toast", NULL }
-/* generic struct to hold shared data */-typedef struct relopt_gen-{- const char *name; /* must be first (used as list termination- * marker) */- const char *desc;- bits32 kinds;- LOCKMODE lockmode;- int namelen;- relopt_type type;-} relopt_gen;--/* holds a parsed value */-typedef struct relopt_value-{- relopt_gen *gen;- bool isset;- union- {- bool bool_val;- int int_val;- double real_val;- int enum_val;- char *string_val; /* allocated separately */- } values;-} relopt_value;--/* reloptions records for specific variable types */-typedef struct relopt_bool-{- relopt_gen gen;- bool default_val;-} relopt_bool;--typedef struct relopt_int-{- relopt_gen gen;- int default_val;- int min;- int max;-} relopt_int;--typedef struct relopt_real-{- relopt_gen gen;- double default_val;- double min;- double max;-} relopt_real;-
/*
- * relopt_enum_elt_def -- One member of the array of acceptable values- * of an enum reloption.+ * backward compatibility aliases so local reloption code of custom validator+ * can work.
*/
-typedef struct relopt_enum_elt_def-{- const char *string_val;- int symbol_val;-} relopt_enum_elt_def;+typedef option_value relopt_value;+typedef fill_string_option fill_string_relopt;+typedef validate_string_option validate_string_relopt;+#define GET_STRING_RELOPTION(optstruct, member) \+ GET_STRING_OPTION(optstruct, member)-typedef struct relopt_enum-{- relopt_gen gen;- relopt_enum_elt_def *members;- int default_val;- const char *detailmsg;- /* null-terminated array of members */-} relopt_enum;-/* validation routines for strings */-typedef void (*validate_string_relopt) (const char *value);-typedef Size (*fill_string_relopt) (const char *value, void *ptr);+/*+ * relopts_validator functions is left for backward compatibility for using+ * with local reloptions. Should not be used elsewhere+ */
/* validation routine for the whole option set */
-typedef void (*relopts_validator) (void *parsed_options, relopt_value *vals, int nvals);--typedef struct relopt_string-{- relopt_gen gen;- int default_len;- bool default_isnull;- validate_string_relopt validate_cb;- fill_string_relopt fill_cb;- char *default_val;-} relopt_string;--/* This is the table datatype for build_reloptions() */-typedef struct-{- const char *optname; /* option's name */- relopt_type opttype; /* option's datatype */- int offset; /* offset of field in result struct */-} relopt_parse_elt;--/* Local reloption definition */-typedef struct local_relopt-{- relopt_gen *option; /* option definition */- int offset; /* offset of parsed value in bytea structure */-} local_relopt;-+typedef void (*relopts_validator) (void *parsed_options, relopt_value *vals,+ int nvals);
/* Structure to hold local reloption data for build_local_reloptions() */
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 */+ List *validators; /* list of relopts_validator callbacks */+ options_spec_set *spec_set; /* Spec Set to store options info */
} local_relopts;
-/*- * Utility macro to get a value for a string reloption once the options- * are parsed. This gets a pointer to the string value itself. "optstruct"- * is the StdRdOptions struct or equivalent, "member" is the struct member- * corresponding to the string option.- */-#define GET_STRING_RELOPTION(optstruct, member) \- ((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 +66,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 +75,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.hindex 5ad4f85940..99664a4114 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.hindex eb56b1c6b8..d3ffe645c3 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.hindex 5d4037f26e..1086723b00 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.cindex 22578b6246..bfb0da3c4a 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,82 @@ 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), false, 6);++ optionsSpecSetAddInt(+ di_relopt_specset, "option_int",+ "Integer option for dummy_index_am",+ AccessExclusiveLock,+ offsetof(DummyIndexOptions, option_int), NULL,+ 10, -10, 100+ );+++ optionsSpecSetAddReal(+ di_relopt_specset, "option_real",+ "Real option for dummy_index_am",+ AccessExclusiveLock,+ offsetof(DummyIndexOptions, option_real), NULL,+ 3.1415, -10, 100+ );++ optionsSpecSetAddBool(+ di_relopt_specset, "option_bool",+ "Boolean option for dummy_index_am",+ AccessExclusiveLock,+ offsetof(DummyIndexOptions, option_bool), NULL, true+ );++ optionsSpecSetAddEnum(di_relopt_specset, "option_enum",+ "Enum option for dummy_index_am",+ AccessExclusiveLock,+ offsetof(DummyIndexOptions, option_enum), NULL,+ 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,+ offsetof(DummyIndexOptions, option_string_val_offset), NULL,+ "DefaultValue", &divalidate_string_option, NULL+ );
/*
* 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,+ offsetof(DummyIndexOptions, option_string_null_offset), NULL,+ NULL, &divalidate_string_option, NULL+ );++ return di_relopt_specset;
}
+
/*
* Build a new index.
*/
@@ -218,19 +217,6 @@ dicostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
*indexPages = 1;
}
-/*- * 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.
*/
@@ -309,7 +295,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;
@@ -323,12 +308,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();-}diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.outindex b6aef6f654..72fee4176b 100644--- a/src/test/regress/expected/reloptions.out+++ b/src/test/regress/expected/reloptions.out@@ -164,7 +164,7 @@ SELECT reloptions FROM pg_class WHERE oid = :toast_oid;
-- Fail on non-existent options 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"
-- Mix TOAST & heap
DROP TABLE reloptions_test;
CREATE TABLE reloptions_test (s VARCHAR) WITH
@@ -183,6 +183,17 @@ SELECT reloptions FROM pg_class WHERE oid = (
{autovacuum_vacuum_cost_delay=23}
(1 row)
+-- Can reset option that is not allowed, but for some reason is already set+UPDATE pg_class+ SET reloptions = '{fillfactor=13,autovacuum_enabled=false,illegal_option=4}'+ WHERE oid = 'reloptions_test'::regclass;+ALTER TABLE reloptions_test RESET (illegal_option);+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;+ reloptions +------------------------------------------+ {fillfactor=13,autovacuum_enabled=false}+(1 row)+
--
-- CREATE INDEX, ALTER INDEX for btrees
--
diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sqlindex 4252b0202f..fadce3384d 100644--- a/src/test/regress/sql/reloptions.sql+++ b/src/test/regress/sql/reloptions.sql@@ -105,6 +105,13 @@ SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
SELECT reloptions FROM pg_class WHERE oid = (
SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+-- Can reset option that is not allowed, but for some reason is already set+UPDATE pg_class+ SET reloptions = '{fillfactor=13,autovacuum_enabled=false,illegal_option=4}'+ WHERE oid = 'reloptions_test'::regclass;+ALTER TABLE reloptions_test RESET (illegal_option);+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;+
--
-- CREATE INDEX, ALTER INDEX for btrees
--
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: dhyan@nataraj.su, andres@anarazel.de, pgsql-hackers@lists.postgresql.org, alvherre@2ndquadrant.com, michael@paquier.xyz, amitlangote09@gmail.com
Subject: Re: [PATCH] New [relation] option engine
In-Reply-To: <40206419.8isWRrtcCr@thinkpad-pgpro>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox