[PATCH] New [relation] option engine
Hi!
I'd like to introduce a patch that reworks options processing.
This patch detaches code for options processing and validating from the code
that uses these options. This will allow to reuse same code in any part of
postgres that may need options. Currently there are three ways of defining
options, and this code is scattered along the source tree, that leads to a
problems: code is hard to understand, hard to maintain, I saw several options
added or changed, and and in one of two cases, there was one or another
mistake done because of code unclearity.
General idea:
There is an object OptionSpec that describes how single option should be
parsed, validated, stored into bytea, etc.
OptionSpecSet is a list of OptionSpecs, that describes all possible options
for certain object (e.g. options for nbtree index relation)
All options related functions that are not depended on context were moved to
src/backend/access/common/options.c. These functions receives OptionSpecSet
(or OptionSpec) and option data that should be processed. And OptionSpecSet
should contain all information that is needed for proper processing.
Options from the perspective of the option engine can exist in four
representation:
- defList - the way they came from SQL parser
- TEXT[] - the way they are stored in pg_class or similar place
- Bytea - the way they stored in C-structure, for caching and using in
postgres code that uses these options
- Value List - is an internal representation that is used while parsing,
validating and converting between representations
See code comments for more info.
There are functions that is used for conversion from one representation to
another:
- optionsDefListToRawValues : defList -> Values
- optionsTextArrayToDefList :TEXT[] -> defList
- optionsTextArrayToRawValues : TEXT[] -> Values
- optionsValuesToTextArray: Values -> TEXT[]
- optionsValuesToBytea: Values -> Bytea
This functions are called from meta-functions that is used when postgres
receives an SQL command for creating or updating options:
- optionsDefListToTextArray - when options are created
- optionsUpdateTexArrayWithDefList - when option are updated.
They also trigger validation while processing.
There are also functions for SpecSet processing:
- allocateOptionsSpecSet
- optionsSpecSetAddBool
- optionsSpecSetAddBool
- optionsSpecSetAddReal
- optionsSpecSetAddEnum
- optionsSpecSetAddString
For index access methods "amoptions" member function that preformed option
processing, were replaced with "amreloptspecset" member function that provided
an SpecSet for reloptions for this AM, so caller can trigger option processing
himself.
For other relation types options have been processing by "fixed" functions, so
these processing were replaced with "fixed" SpecSet providers + processing
using that SpecSet. Later on these should be moved to access method the same
way it is done in indexes. I plan to do it after this patch is commit.
As for local options, that is used of opclass options, I kept all current API,
but made this API a wrapper around new option engine. Local options should be
moved to unified option engine API later on. I hope to do it too.
This patch does not change any of postgres behaviour (even if this behaviour
is not quite right). The only change is text of the warning for unexisting
option in toast namespace. But I hope this change is for better.
The only problem I am not sure how to solve is an obtaining a LockMode.
To get a LockMode for option , you need a SpecSet. For indexes we get SpecSets
via AccessMethod. To get access for AccessMethod, you need relation C-
structure. To get relation structure you need open relation with NoLock mode.
But if I do it, I get Assert in relation_open. (There were no such Assert when
I started this work, it appeared later).
All code related to this issue is marked with FIXME comments. I've commented
that Assert out, but not quite sure I was right. Here I need a help of more
experienced members of community.
This quite a big patch. Unfortunately it can't be split into smaller parts.
I also suggest to consider this patch as an implementation of general idea,
that works well. I have ideas in mind to make it better, but it will be
infinite improvement process that will never lead to final commit. So if the
concept is good and implementation is good enough, I suggest to commit it, and
make it better later on by smaller patches over it. If it is not good enough,
let me know, I will try to make it good.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
new_options_take_two_v01.difftext/x-patch; charset=UTF-8; name=new_options_take_two_v01.diffDownload
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 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.c
index 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.out
index 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 length
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 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.c
index 5a37508c4b..1bfb98f54e 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -2005,7 +2005,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 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.c
index fc3ce6a53a..185a726783 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.c
index ba78ecff66..7aca22e789 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.
@@ -1764,3 +1745,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/Makefile
index 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.c
new file mode 100644
index 0000000000..08d941c8b8
--- /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;
+
+ 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.c
index 1c02bf03a3..9d703cc509 100644
--- a/src/backend/access/common/relation.c
+++ b/src/backend/access/common/relation.c
@@ -65,9 +65,13 @@ relation_open(Oid relationId, LOCKMODE lockmode)
* If we didn't get the lock ourselves, assert that caller holds one,
* except in bootstrap mode where no locks are used.
*/
- Assert(lockmode != NoLock ||
- IsBootstrapProcessingMode() ||
- CheckRelationLockedByMe(r, AccessShareLock, true));
+
+// FIXME We need NoLock mode to get AM data when choosing Lock for
+// attoptions is changed. See ProcessUtilitySlow problems comes from there
+// This is a dirty hack, we need better solution for this case;
+// Assert(lockmode != NoLock ||
+// IsBootstrapProcessingMode() ||
+// CheckRelationLockedByMe(r, AccessShareLock, true));
/* Make note that we've accessed a temporary relation */
if (RelationUsesLocalBuffers(r))
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index d592655258..a79fbf0e69 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,380 +91,8 @@
* value has no effect until the next VACUUM, so no need for stronger lock.
*/
-static relopt_bool boolRelOpts[] =
-{
- {
- {
- "autosummarize",
- "Enables automatic summarization on this BRIN index",
- RELOPT_KIND_BRIN,
- AccessExclusiveLock
- },
- false
- },
- {
- {
- "autovacuum_enabled",
- "Enables autovacuum in this relation",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- true
- },
- {
- {
- "user_catalog_table",
- "Declare a table as an additional catalog table, e.g. for the purpose of logical replication",
- RELOPT_KIND_HEAP,
- AccessExclusiveLock
- },
- false
- },
- {
- {
- "fastupdate",
- "Enables \"fast update\" feature for this GIN index",
- RELOPT_KIND_GIN,
- AccessExclusiveLock
- },
- true
- },
- {
- {
- "security_barrier",
- "View acts as a row security barrier",
- RELOPT_KIND_VIEW,
- AccessExclusiveLock
- },
- false
- },
- {
- {
- "vacuum_truncate",
- "Enables vacuum to truncate empty pages at the end of this table",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- true
- },
- {
- {
- "deduplicate_items",
- "Enables \"deduplicate items\" feature for this btree index",
- RELOPT_KIND_BTREE,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- true
- },
- /* list terminator */
- {{NULL}}
-};
-
-static relopt_int intRelOpts[] =
-{
- {
- {
- "fillfactor",
- "Packs table pages only to this percentage",
- RELOPT_KIND_HEAP,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100
- },
- {
- {
- "fillfactor",
- "Packs btree index pages only to this percentage",
- RELOPT_KIND_BTREE,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
- },
- {
- {
- "fillfactor",
- "Packs hash index pages only to this percentage",
- RELOPT_KIND_HASH,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100
- },
- {
- {
- "fillfactor",
- "Packs gist index pages only to this percentage",
- RELOPT_KIND_GIST,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
- },
- {
- {
- "fillfactor",
- "Packs spgist index pages only to this percentage",
- RELOPT_KIND_SPGIST,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100
- },
- {
- {
- "autovacuum_vacuum_threshold",
- "Minimum number of tuple updates or deletes prior to vacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0, INT_MAX
- },
- {
- {
- "autovacuum_vacuum_insert_threshold",
- "Minimum number of tuple inserts prior to vacuum, or -1 to disable insert vacuums",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -2, -1, INT_MAX
- },
- {
- {
- "autovacuum_analyze_threshold",
- "Minimum number of tuple inserts, updates or deletes prior to analyze",
- RELOPT_KIND_HEAP,
- ShareUpdateExclusiveLock
- },
- -1, 0, INT_MAX
- },
- {
- {
- "autovacuum_vacuum_cost_limit",
- "Vacuum cost amount available before napping, for autovacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 1, 10000
- },
- {
- {
- "autovacuum_freeze_min_age",
- "Minimum age at which VACUUM should freeze a table row, for autovacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0, 1000000000
- },
- {
- {
- "autovacuum_multixact_freeze_min_age",
- "Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0, 1000000000
- },
- {
- {
- "autovacuum_freeze_max_age",
- "Age at which to autovacuum a table to prevent transaction ID wraparound",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 100000, 2000000000
- },
- {
- {
- "autovacuum_multixact_freeze_max_age",
- "Multixact age at which to autovacuum a table to prevent multixact wraparound",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 10000, 2000000000
- },
- {
- {
- "autovacuum_freeze_table_age",
- "Age at which VACUUM should perform a full table sweep to freeze row versions",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- }, -1, 0, 2000000000
- },
- {
- {
- "autovacuum_multixact_freeze_table_age",
- "Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- }, -1, 0, 2000000000
- },
- {
- {
- "log_autovacuum_min_duration",
- "Sets the minimum execution time above which autovacuum actions will be logged",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, -1, INT_MAX
- },
- {
- {
- "toast_tuple_target",
- "Sets the target tuple length at which external columns will be toasted",
- RELOPT_KIND_HEAP,
- ShareUpdateExclusiveLock
- },
- TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
- },
- {
- {
- "pages_per_range",
- "Number of pages that each page range covers in a BRIN index",
- RELOPT_KIND_BRIN,
- AccessExclusiveLock
- }, 128, 1, 131072
- },
- {
- {
- "gin_pending_list_limit",
- "Maximum size of the pending list for this GIN index, in kilobytes.",
- RELOPT_KIND_GIN,
- AccessExclusiveLock
- },
- -1, 64, MAX_KILOBYTES
- },
- {
- {
- "effective_io_concurrency",
- "Number of simultaneous requests that can be handled efficiently by the disk subsystem.",
- RELOPT_KIND_TABLESPACE,
- ShareUpdateExclusiveLock
- },
-#ifdef USE_PREFETCH
- -1, 0, MAX_IO_CONCURRENCY
-#else
- 0, 0, 0
-#endif
- },
- {
- {
- "maintenance_io_concurrency",
- "Number of simultaneous requests that can be handled efficiently by the disk subsystem for maintenance work.",
- RELOPT_KIND_TABLESPACE,
- ShareUpdateExclusiveLock
- },
-#ifdef USE_PREFETCH
- -1, 0, MAX_IO_CONCURRENCY
-#else
- 0, 0, 0
-#endif
- },
- {
- {
- "parallel_workers",
- "Number of parallel processes that can be used per executor node for this relation.",
- RELOPT_KIND_HEAP,
- ShareUpdateExclusiveLock
- },
- -1, 0, 1024
- },
-
- /* list terminator */
- {{NULL}}
-};
-
-static relopt_real realRelOpts[] =
-{
- {
- {
- "autovacuum_vacuum_cost_delay",
- "Vacuum cost delay in milliseconds, for autovacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, 100.0
- },
- {
- {
- "autovacuum_vacuum_scale_factor",
- "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, 100.0
- },
- {
- {
- "autovacuum_vacuum_insert_scale_factor",
- "Number of tuple inserts prior to vacuum as a fraction of reltuples",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, 100.0
- },
- {
- {
- "autovacuum_analyze_scale_factor",
- "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
- RELOPT_KIND_HEAP,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, 100.0
- },
- {
- {
- "seq_page_cost",
- "Sets the planner's estimate of the cost of a sequentially fetched disk page.",
- RELOPT_KIND_TABLESPACE,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, DBL_MAX
- },
- {
- {
- "random_page_cost",
- "Sets the planner's estimate of the cost of a nonsequentially fetched disk page.",
- RELOPT_KIND_TABLESPACE,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, DBL_MAX
- },
- {
- {
- "n_distinct",
- "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
- RELOPT_KIND_ATTRIBUTE,
- ShareUpdateExclusiveLock
- },
- 0, -1.0, DBL_MAX
- },
- {
- {
- "n_distinct_inherited",
- "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
- RELOPT_KIND_ATTRIBUTE,
- ShareUpdateExclusiveLock
- },
- 0, -1.0, DBL_MAX
- },
- {
- {
- "vacuum_cleanup_index_scale_factor",
- "Deprecated B-Tree parameter.",
- RELOPT_KIND_BTREE,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, 1e10
- },
- /* list terminator */
- {{NULL}}
-};
-
/* values from StdRdOptIndexCleanup */
-relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -480,17 +106,8 @@ relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-relopt_enum_elt_def gistBufferingOptValues[] =
-{
- {"auto", GIST_OPTION_BUFFERING_AUTO},
- {"on", GIST_OPTION_BUFFERING_ON},
- {"off", GIST_OPTION_BUFFERING_OFF},
- {(const char *) NULL} /* list terminator */
-};
-
/* values from ViewOptCheckOption */
-relopt_enum_elt_def viewCheckOptValues[] =
+opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -498,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
@@ -726,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);
}
/*
@@ -742,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
@@ -858,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);
}
/*
@@ -909,453 +164,59 @@ add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_
*/
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;
+ 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;
+ optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
- /* Nothing to do if no options */
- if (!PointerIsValid(DatumGetPointer(options)))
- return result;
-
- array = DatumGetArrayTypeP(options);
-
- deconstruct_array(array, TEXTOID, -1, false, TYPALIGN_INT,
- &optiondatums, NULL, &noptions);
-
- for (i = 0; i < noptions; i++)
- {
- char *s;
- char *p;
- Node *val = NULL;
+}
- s = TextDatumGetCString(optiondatums[i]);
- p = strchr(s, '=');
- if (p)
- {
- *p++ = '\0';
- val = (Node *) makeString(pstrdup(p));
- }
- result = lappend(result, makeDefElem(pstrdup(s), val, -1));
- }
+/*
+ * 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);
}
/*
@@ -1372,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,
@@ -1391,545 +253,268 @@ 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)
-{
- 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);
-}
-
-/*
- * 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.
- */
-static relopt_value *
-parseRelOptions(Datum options, bool validate, relopt_kind kind,
- int *numrelopts)
-{
- relopt_value *reloptions = NULL;
- int numoptions = 0;
- int i;
- int j;
-
- if (need_initialization)
- initialize_reloptions();
-
- /* Build a list of expected options, based on kind */
-
- for (i = 0; relOpts[i]; i++)
- if (relOpts[i]->kinds & kind)
- numoptions++;
-
- if (numoptions > 0)
- {
- reloptions = palloc(numoptions * sizeof(relopt_value));
-
- for (i = 0, j = 0; relOpts[i]; i++)
- {
- if (relOpts[i]->kinds & kind)
- {
- reloptions[j].gen = relOpts[i];
- reloptions[j].isset = false;
- j++;
- }
- }
- }
-
- /* Done if no options */
- if (PointerIsValid(DatumGetPointer(options)))
- parseRelOptionsInternal(options, validate, reloptions, numoptions);
-
- *numrelopts = numoptions;
- return reloptions;
-}
-
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
+void
+oid_postvalidate(option_value* value)
{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
-
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
-
- if (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
-
- return values;
+ if (value->values.bool_val)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("tables declared WITH OIDS are not supported")));
}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * 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 void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
-
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
-
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
+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);
}
- 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;
-
- for (i = 0; i < numoptions; i++)
- {
- relopt_value *optval = &options[i];
-
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
-
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
-
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
+ 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);
}
- return palloc0(size);
+ return stdrd_relopt_spec_set;
}
-/*
- * 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;
+static options_spec_set *heap_relopt_spec_set = NULL;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
+static options_spec_set *toast_relopt_spec_set = NULL;
-/*
- * Option parser for anything that uses StdRdOptions.
- */
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- 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));
+ 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;
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
-
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
-
- pfree(options);
+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 rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1940,157 +525,201 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ /*
+ * Kind of ugly conversion here for backward compatibility.
+ * Would be removed while moving opclass options to options.c API
+ */
- i++;
- }
+ if (validate && relopts->validators)
+ {
+ int val_count = 0;
+ int i;
+ option_value *val_array;
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ foreach(lc, values)
+ val_count++;
+ val_array = palloc(sizeof(option_value)*val_count);
- foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ i=0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (elems)
- pfree(elems);
+ foreach(lc, relopts->validators)
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
+ pfree(val_array);
+ }
return opts;
-}
-/*
- * 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
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-bytea *
-view_reloptions(Datum reloptions, bool validate)
+static options_spec_set *view_relopt_spec_set = NULL;
+
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- static const relopt_parse_elt tab[] = {
- {"security_barrier", RELOPT_TYPE_BOOL,
- offsetof(ViewOptions, security_barrier)},
- {"check_option", RELOPT_TYPE_ENUM,
- offsetof(ViewOptions, check_option)}
- };
-
- return (bytea *) build_reloptions(reloptions, validate,
- RELOPT_KIND_VIEW,
- sizeof(ViewOptions),
- tab, lengthof(tab));
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
+
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 2);
+
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
+
+ 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 view_relopt_spec_set;
}
/*
- * Parse options for heaps, views and toast tables.
+ * get_attribute_options_spec_set
+ * Returns an options spec set for heap attributes
*/
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
- switch (relkind)
- {
- 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;
- }
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
/*
- * 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)
+ * get_tablespace_options_spec_set
+ * Returns an options spec set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- Assert(amoptions != NULL);
+ if (!tablespace_options_spec_set)
+ {
+ tablespace_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(TableSpaceOpts), false, 4);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ optionsSpecSetAddReal(tablespace_options_spec_set,
+ "random_page_cost",
+ "Sets the planner's estimate of the cost of a nonsequentially fetched disk page",
+ ShareUpdateExclusiveLock,
+ offsetof(TableSpaceOpts, random_page_cost),
+ NULL, -1, 0.0, DBL_MAX);
- return amoptions(reloptions, validate);
-}
+ optionsSpecSetAddReal(tablespace_options_spec_set, "seq_page_cost",
+ "Sets the planner's estimate of the cost of a sequentially fetched disk page",
+ ShareUpdateExclusiveLock,
+ offsetof(TableSpaceOpts, seq_page_cost),
+ NULL, -1, 0.0, DBL_MAX);
-/*
- * Option parser for attribute reloptions
- */
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
-{
- static const relopt_parse_elt tab[] = {
- {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
- {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
- };
-
- return (bytea *) build_reloptions(reloptions, validate,
- RELOPT_KIND_ATTRIBUTE,
- sizeof(AttributeOpts),
- tab, lengthof(tab));
+ optionsSpecSetAddInt(tablespace_options_spec_set, "effective_io_concurrency",
+ "Number of simultaneous requests that can be handled efficiently by the disk subsystem",
+ ShareUpdateExclusiveLock,
+ 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 tablespace 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 *
-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.
@@ -2099,33 +728,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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 6a651d8397..65db3c0acc 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.c
index 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.c
index 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.c
index 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.c
index 560dcc87a2..48e7ac938f 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -19,6 +19,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
+#include "access/options.h"
#include "access/sysattr.h"
#include "access/tableam.h"
#include "access/xact.h"
@@ -531,7 +532,7 @@ DefineIndex(Oid relationId,
Form_pg_am accessMethodForm;
IndexAmRoutine *amRoutine;
bool amcanorder;
- amoptions_function amoptions;
+ amreloptspecset_function amreloptspecsetfn;
bool partitioned;
bool safe_index;
Datum reloptions;
@@ -837,7 +838,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -851,10 +852,17 @@ 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);
/*
* Prepare arguments for index_create, primarily an IndexInfo structure.
@@ -1987,8 +1995,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
indexInfo->ii_OpclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
attn++;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3e83f375b5..e3e3cb70c6 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"
@@ -664,7 +665,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;
@@ -812,19 +812,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)
@@ -4077,7 +4094,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);
@@ -4119,7 +4136,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.
@@ -4340,9 +4357,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;
@@ -8131,12 +8152,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));
@@ -14092,12 +14114,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 */
@@ -14117,38 +14140,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),
@@ -14158,11 +14201,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;
@@ -14237,20 +14284,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.c
index b2ccf5e06e..a883d89c14 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -363,10 +363,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
@@ -1091,11 +1090,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.c
index 294e22c78c..c60ed798b8 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -78,7 +78,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
if (isnull)
fdw->options = NIL;
else
- fdw->options = untransformRelOptions(datum);
+ fdw->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -165,7 +165,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
if (isnull)
server->options = NIL;
else
- server->options = untransformRelOptions(datum);
+ server->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -233,7 +233,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -270,7 +270,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -303,7 +303,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -572,7 +572,7 @@ pg_options_to_table(PG_FUNCTION_ARGS)
Datum array = PG_GETARG_DATUM(0);
deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo,
- untransformRelOptions(array));
+ optionsTextArrayToDefList(array));
return (Datum) 0;
}
@@ -643,7 +643,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 99efa26ce4..5ac6b9b99d 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.c
index 83e4e37c78..df3f85de42 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1156,6 +1156,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;
@@ -1179,15 +1180,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);
@@ -1296,9 +1298,12 @@ ProcessUtilitySlow(ParseState *pstate,
* lock on (for example) a relation on which we have no
* permissions.
*/
- lockmode = AlterTableGetLockLevel(atstmt->cmds);
- relid = AlterTableLookupRelation(atstmt, lockmode);
-
+ relid = AlterTableLookupRelation(atstmt, NoLock); // FIXME!
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 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.c
index 2707fed12f..5dfa6020a3 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -459,7 +459,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -474,11 +474,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;
@@ -489,7 +489,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
new file mode 100644
index 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.h
index 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.h
index 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.h
index 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.h
index 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.c
index 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.out
index 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.sql
index 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
--
Hi,
On 2022-02-14 00:43:36 +0300, Nikolay Shaplov wrote:
I'd like to introduce a patch that reworks options processing.
This doesn't apply anymore: http://cfbot.cputube.org/patch_37_3536.log
Given that this patch has been submitted just to the last CF and that there's
been no action on it, I don't see this going into 15. Therefore I'd like to
move it to the next CF?
Greetings,
Andres Freund
В письме от вторник, 22 марта 2022 г. 03:46:10 MSK пользователь Andres Freund
написал:
I'd like to introduce a patch that reworks options processing.
This doesn't apply anymore: http://cfbot.cputube.org/patch_37_3536.log
Thank you for sending this notice!
I've updated the patch to include new changes, that have been made in postgres
recently.
Given that this patch has been submitted just to the last CF and that
there's been no action on it, I don't see this going into 15.
Yes it is not to be added in 15 for sure.
Therefore I'd like to move it to the next CF?
The only reason it can be kept in current CF, is to keep it in front of the
eyes of potential reviewers for one more week. So it is up to you, if you
consider it does not worth it, you can move it now. No great harm would be
done then.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
new_options_take_two_v01a.difftext/x-patch; charset=UTF-8; name=new_options_take_two_v01a.diffDownload
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 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.c
index 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.out
index 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 length
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 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.c
index 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.c
index 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.c
index 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.c
index 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/Makefile
index 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.c
new file mode 100644
index 0000000000..08d941c8b8
--- /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;
+
+ 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index cd30f15eba..3ed5370037 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -19,6 +19,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
+#include "access/options.h"
#include "access/sysattr.h"
#include "access/tableam.h"
#include "access/xact.h"
@@ -531,7 +532,7 @@ DefineIndex(Oid relationId,
Form_pg_am accessMethodForm;
IndexAmRoutine *amRoutine;
bool amcanorder;
- amoptions_function amoptions;
+ amreloptspecset_function amreloptspecsetfn;
bool partitioned;
bool safe_index;
Datum reloptions;
@@ -837,7 +838,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -851,10 +852,17 @@ 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);
/*
* Prepare arguments for index_create, primarily an IndexInfo structure.
@@ -1987,8 +1995,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
indexInfo->ii_OpclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
attn++;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 80faae985e..bfced0bc37 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"
@@ -671,7 +672,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;
@@ -819,19 +819,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)
@@ -4103,7 +4120,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);
@@ -4145,7 +4162,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.
@@ -4366,9 +4383,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;
@@ -8131,12 +8152,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));
@@ -14115,12 +14137,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 */
@@ -14140,38 +14163,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),
@@ -14181,11 +14224,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;
@@ -14260,20 +14307,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.c
index 40514ab550..5ddc37033a 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -363,10 +363,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
@@ -1100,11 +1099,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.c
index 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.c
index 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.c
index 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.c
index 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.c
index fbd11883e1..21b7e89b5f 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -460,7 +460,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -475,11 +475,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;
@@ -490,7 +490,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
new file mode 100644
index 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.h
index 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.h
index 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.h
index 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.h
index 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.c
index 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.out
index 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.sql
index 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
--
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:
new_options_take_two_v01b.difftext/x-patch; charset=UTF-8; name=new_options_take_two_v01b.diffDownload
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 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.c
index 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.out
index 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 length
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 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.c
index 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.c
index 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.c
index 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.c
index 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/Makefile
index 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.c
new file mode 100644
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.c
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
new file mode 100644
index 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.h
index 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.h
index 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.h
index 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.h
index 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.c
index 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.out
index 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.sql
index 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
--
В письме от воскресенье, 27 марта 2022 г. 15:19:41 MSK пользователь Nikolay
Shaplov написал:
A minor fix to get rid of maybe-uninitialized warnings.
Same patch, but properly rebased to current master head.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
new_options_take_two_v01c.difftext/x-patch; charset=utf-8; name=new_options_take_two_v01c.diffDownload
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 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.c
index 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.out
index 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 length
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 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.c
index a561d1d652..2aafe2df83 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1977,7 +1977,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 4773cadec0..1de15ce09b 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -195,7 +195,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 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.c
index 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/Makefile
index 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.c
new file mode 100644
index 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.c
index 382a42ff7d..579acbbaad 100644
--- a/src/backend/access/common/relation.c
+++ b/src/backend/access/common/relation.c
@@ -65,9 +65,13 @@ relation_open(Oid relationId, LOCKMODE lockmode)
* If we didn't get the lock ourselves, assert that caller holds one,
* except in bootstrap mode where no locks are used.
*/
- Assert(lockmode != NoLock ||
- IsBootstrapProcessingMode() ||
- CheckRelationLockedByMe(r, AccessShareLock, true));
+
+// FIXME We need NoLock mode to get AM data when choosing Lock for
+// attoptions is changed. See ProcessUtilitySlow problems comes from there
+// This is a dirty hack, we need better solution for this case;
+// Assert(lockmode != NoLock ||
+// IsBootstrapProcessingMode() ||
+// CheckRelationLockedByMe(r, AccessShareLock, true));
/* Make note that we've accessed a temporary relation */
if (RelationUsesLocalBuffers(r))
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 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.c
index 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.c
index 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.c
index 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.c
index fd1a7119b6..31a7117bf2 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.c
index fe37bc47cb..a39c026f4c 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.c
index 06131f23d4..7a777505c0 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.c
index ff260c393a..a08004c045 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,23 +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.c
index a171ca8a08..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,22 +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).
@@ -1348,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.c
index 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.c
index 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.c
index 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.c
index 2cd8546d47..23a39c45b1 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)
@@ -4102,7 +4119,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);
@@ -4144,7 +4161,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.
@@ -4365,9 +4382,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;
@@ -8155,12 +8176,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));
@@ -14140,12 +14162,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 */
@@ -14165,38 +14188,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),
@@ -14206,11 +14249,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;
@@ -14285,20 +14332,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.c
index 40514ab550..5ddc37033a 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -363,10 +363,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
@@ -1100,11 +1099,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.c
index 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.c
index 2826559d09..108085dd97 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1759,7 +1759,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;
@@ -1823,7 +1823,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 0e7b7b3138..f41b195022 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1163,6 +1163,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;
@@ -1186,15 +1187,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);
@@ -1303,9 +1305,12 @@ ProcessUtilitySlow(ParseState *pstate,
* lock on (for example) a relation on which we have no
* permissions.
*/
- lockmode = AlterTableGetLockLevel(atstmt->cmds);
- relid = AlterTableLookupRelation(atstmt, lockmode);
-
+ relid = AlterTableLookupRelation(atstmt, NoLock); // FIXME!
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 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.c
index 43f14c233d..d59fb2e7b0 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.c
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
index da372841c4..12ff5ffcbb 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -472,6 +471,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 93f8267b48..608d94c0bf 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1254,7 +1254,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void * btgetreloptspecset (void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 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.h
index 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.h
index 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.h
index 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.h
index 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.c
index 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.out
index 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.sql
index 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
--
В письме от суббота, 16 апреля 2022 г. 17:15:34 MSK пользователь Nikolay
Shaplov написал:
Changed NoLock to AccessShareLock. This will remove last FIXME from the patch.
As more experienced pg-developers told me, you can call relation_open with
AccessShareLock, on a table you are going to work with, any time you like,
without being ashamed about it.
But this lock is visible when you have prepared transaction, so have to update
twophase test of test_decoding extension.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
new_options_take_two_v02.difftext/x-patch; charset=UTF-8; name=new_options_take_two_v02.diffDownload
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 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.c
index 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.out
index 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 length
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 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.c
index a561d1d652..2aafe2df83 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1977,7 +1977,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 4773cadec0..1de15ce09b 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -195,7 +195,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index e89dc74a5e..af46f0a3ee 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 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/Makefile
index 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.c
new file mode 100644
index 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/reloptions.c b/src/backend/access/common/reloptions.c
index 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.c
index 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.c
index 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.c
index 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.c
index fd1a7119b6..31a7117bf2 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.c
index fe37bc47cb..a39c026f4c 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.c
index 06131f23d4..7a777505c0 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.c
index ff260c393a..a08004c045 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,23 +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.c
index a171ca8a08..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,22 +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).
@@ -1348,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.c
index 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.c
index 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.c
index 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.c
index 2cd8546d47..3c82dc4a9e 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)
@@ -4102,7 +4119,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);
@@ -4144,7 +4161,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.
@@ -4365,9 +4382,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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8155,12 +8176,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));
@@ -14140,12 +14162,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 */
@@ -14165,38 +14188,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),
@@ -14206,11 +14249,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;
@@ -14285,20 +14332,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.c
index 40514ab550..5ddc37033a 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -363,10 +363,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
@@ -1100,11 +1099,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.c
index 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.c
index 1a64a52279..cd44d45f66 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1759,7 +1759,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;
@@ -1823,7 +1823,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 0e7b7b3138..8ae86b69ad 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1163,6 +1163,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;
@@ -1186,15 +1187,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);
@@ -1303,9 +1305,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 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.c
index 43f14c233d..d59fb2e7b0 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.c
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
index 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.h
index da372841c4..12ff5ffcbb 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -472,6 +471,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 93f8267b48..608d94c0bf 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1254,7 +1254,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void * btgetreloptspecset (void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 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.h
index 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.h
index 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.h
index 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.h
index 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.c
index 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.out
index 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.sql
index 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
--
В письме от среда, 4 мая 2022 г. 18:15:51 MSK пользователь Nikolay Shaplov
написал:
Rebased patch, so it can be applied to current master again.
Added static keyword to enum option items definition as it have been done in
09cd33f4
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
new_options_take_two_v02a.difftext/x-patch; charset=UTF-8; name=new_options_take_two_v02a.diffDownload
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 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.c
index 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.out
index 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 length
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 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.c
index a561d1d652..2aafe2df83 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1977,7 +1977,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 4773cadec0..1de15ce09b 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -195,7 +195,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index e89dc74a5e..af46f0a3ee 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 52f171772d..a9e3185bfd 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.
@@ -1785,3 +1766,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/Makefile
index 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.c
new file mode 100644
index 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/reloptions.c b/src/backend/access/common/reloptions.c
index 24211807ca..81ca078505 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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -489,17 +106,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -507,225 +115,9 @@ static 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.c
index 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.c
index 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.c
index d4bf0c7563..3fbc0426cc 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 */
+static 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.c
index fd1a7119b6..31a7117bf2 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.c
index fe37bc47cb..a39c026f4c 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.c
index 06131f23d4..7a777505c0 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.c
index ff260c393a..a08004c045 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,23 +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.c
index a171ca8a08..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,22 +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).
@@ -1348,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.c
index 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.c
index 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.c
index eac13ac0b7..edb91ba567 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;
@@ -847,7 +848,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -861,11 +862,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
@@ -2039,8 +2048,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
indexInfo->ii_OpclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
attn++;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 2de0ebacec..fefe6344c2 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)
@@ -4102,7 +4119,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);
@@ -4144,7 +4161,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.
@@ -4365,9 +4382,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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8155,12 +8176,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));
@@ -14141,12 +14163,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 */
@@ -14166,38 +14189,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),
@@ -14207,11 +14250,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;
@@ -14286,20 +14333,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.c
index 00ca397fe8..9be5cb521f 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
@@ -1100,11 +1099,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.c
index 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.c
index 1a64a52279..cd44d45f66 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1759,7 +1759,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;
@@ -1823,7 +1823,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 0e7b7b3138..8ae86b69ad 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1163,6 +1163,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;
@@ -1186,15 +1187,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);
@@ -1303,9 +1305,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 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.c
index 60e72f9e8b..3a2411e131 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.c
index 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.h
index 0b89f399f0..0ce853b0d6 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.h
index 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.h
index 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.h
index 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.h
index 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.h
index da372841c4..12ff5ffcbb 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -472,6 +471,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 93f8267b48..608d94c0bf 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1254,7 +1254,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void * btgetreloptspecset (void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 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.h
index 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.h
index 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.h
index 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.h
index 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.c
index 22578b6246..67a8e880b6 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[] =
+static 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.out
index 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.sql
index 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
--
I'm sorry if you've already said this elsewhere, but can you please
state what is the *intention* of this patchset? If it's a pure
refactoring (but I don't think it is), then it's a net loss, because
after pgindent it summarizes as:
58 files changed, 2714 insertions(+), 2368 deletions(-)
so we end up 500+ lines worse than the initial story. However, I
suspect that's not the final situation, since I saw a comment somewhere
that opclass options have to be rewritten to modify this mechanism, and
I suspect that will remove a few lines. And you maybe have a more
ambitious goal. But what is it?
Please pgindent your code for the next submission, making sure to add
your new typedef(s) to typedefs.list so that it doesn't generate stupid
spaces. After pgindenting you'll notice the argument lists of some
functions look bad (cf. commit c4f113e8fef9). Please fix that too.
I notice that you kept the commentary about lock levels in the place
where they were previously defined. This is not good. Please move each
explanation next to the place where each option is defined.
For next time, please use "git format-patch" for submission, and write a
tentative commit message. The committer may or may not use your
proposed commit message, but with it they will know what you're trying
to achieve.
The translatability marker for detailmsg for enums is wrong AFAICT. You
need gettext_noop() around the strings themselves IIRC. I think you
need to get rid of the _() call around the variable that receives that
value and use errdetail() instead of errdetail_internal(), to avoid
double-translating it; but I'm not 100% sure. Please experiment with
"make update-po" until you get the messages in the .po file.
You don't need braces around single-statement blocks.
Thanks
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
"World domination is proceeding according to plan" (Andrew Morton)
В письме от воскресенье, 15 мая 2022 г. 15:25:47 MSK пользователь Alvaro
Herrera написал:
I'm sorry if you've already said this elsewhere, but can you please
state what is the *intention* of this patchset? If it's a pure
refactoring (but I don't think it is), then it's a net loss, because
after pgindent it summarizes as:58 files changed, 2714 insertions(+), 2368 deletions(-)
so we end up 500+ lines worse than the initial story. However, I
suspect that's not the final situation, since I saw a comment somewhere
that opclass options have to be rewritten to modify this mechanism, and
I suspect that will remove a few lines. And you maybe have a more
ambitious goal. But what is it?
My initial goal was to make options code reusable for any types of options
(not only reloptions). While working with this goal I came to conclusion that
I have to create completely new option engine that will be used anywhere
options (name=value) is needed. This will solve following problems:
- provide unified API for options definition. Currently postgres have core-AM
options, contrib-AM options and local options for opclasses, each have their
own way to define options. This patch will allow to use one API for them all
(for opclasses it is still WIP)
- Currently core-AM options are partly defined in reloptions.c and partly in AM
code. This is error prone. This patch fixes that.
- For indexes option definition is moved into AM code, where they should be.
For heap it should be moved into AM code later.
- There is no difference for core-AM indexes, and contrib-AM indexes options.
They use same API.
I also tried to write detailed commit message as you've suggested. There my
goals is described in more detailed way.
Please pgindent your code for the next submission, making sure to add
your new typedef(s) to typedefs.list so that it doesn't generate stupid
spaces. After pgindenting you'll notice the argument lists of some
functions look bad (cf. commit c4f113e8fef9). Please fix that too.
I've tried to pgindent. Hope I did it well. I've manually edited all code
lines (not string consts) that were longer then 80 characters, afterwards.
Hope it was right decision
I notice that you kept the commentary about lock levels in the place
where they were previously defined. This is not good. Please move each
explanation next to the place where each option is defined.
You are right. Tried to find better place for it.
I also noticed that I've missed updating initial comment for reloptions.c.
Will update it this week, meanwhile will send a patch version without changing
that comment, in order not to slow anything down.
For next time, please use "git format-patch" for submission, and write a
tentative commit message. The committer may or may not use your
proposed commit message, but with it they will know what you're trying
to achieve.
Done.
The translatability marker for detailmsg for enums is wrong AFAICT. You
need gettext_noop() around the strings themselves IIRC. I think you
need to get rid of the _() call around the variable that receives that
value and use errdetail() instead of errdetail_internal(), to avoid
double-translating it; but I'm not 100% sure. Please experiment with
"make update-po" until you get the messages in the .po file.
That part of code was not written by me. It was added while enum options were
commit. Then I've just copied it to this patch. I do not quite understand how
does it works. But I can say that update-po works well for enum detailmsg, and
we actually have gettext_noop(), but it is used while calling
optionsSpecSetAddEnum, not when error message is actually printed. But I guess
it do the trick.
You don't need braces around single-statement blocks.
Tried to remove all I've found.
Thanks
Thank you for answering.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
new_options_take_two_v03.patchtext/x-patch; charset=UTF-8; name=new_options_take_two_v03.patchDownload
From 6b5bcb998a3e2d809113ee1f41d4ea961aa6a636 Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Mon, 16 May 2022 17:13:31 +0300
Subject: [PATCH] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 111 +-
contrib/bloom/expected/bloom.out | 3 +
contrib/bloom/sql/bloom.sql | 3 +
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
src/backend/access/brin/brin.c | 47 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/options.c | 1364 ++++++++++
src/backend/access/common/reloptions.c | 2317 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 19 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/access/transam/xact.c | 2 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/bin/pg_basebackup/streamutil.c | 2 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 2 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 262 ++
src/include/access/reloptions.h | 205 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 148 +-
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
src/tools/pgindent/typedefs.list | 28 +-
49 files changed, 2636 insertions(+), 2379 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 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.c
index a434cf93ef..6587a721c4 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,24 @@ 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 +452,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.out
index 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 length
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 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.c
index a561d1d652..2aafe2df83 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1977,7 +1977,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 4773cadec0..1de15ce09b 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -195,7 +195,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index e89dc74a5e..af46f0a3ee 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 52f171772d..a1eb51bcd6 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.
@@ -1785,3 +1766,29 @@ 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/Makefile
index 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.c
new file mode 100644
index 0000000000..499e98c274
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1364 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 unparsed 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 values 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 conversion.
+ */
+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/reloptions.c b/src/backend/access/common/reloptions.c
index 24211807ca..b01462d0cd 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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -489,17 +106,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -507,225 +115,9 @@ static 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,504 +152,71 @@ 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);
-}
-
-/*
- * add_local_int_reloption
- * Add a new local integer reloption
- *
- * '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;
+ optionsSpecSetAddBool(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val);
}
-
-/*
- * 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;
-}
-
-
/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
+ * add_local_int_reloption
+ * Add a new local integer reloption
+ *
+ * 'offset' is offset of int-typed field.
*/
-List *
-untransformRelOptions(Datum options)
+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)
{
- 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);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
+}
- deconstruct_array(array, TEXTOID, -1, false, TYPALIGN_INT,
- &optiondatums, NULL, &noptions);
+/*
+ * 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)
+{
+ 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,545 +253,308 @@ 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)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- 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++;
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (numoptions > 0)
- {
- reloptions = palloc(numoptions * sizeof(relopt_value));
-
- for (i = 0, j = 0; relOpts[i]; i++)
- {
- if (relOpts[i]->kinds & kind)
- {
- reloptions[j].gen = relOpts[i];
- reloptions[j].isset = false;
- j++;
- }
- }
- }
-
- /* Done if no options */
- if (PointerIsValid(DatumGetPointer(options)))
- parseRelOptionsInternal(options, validate, reloptions, numoptions);
-
- *numrelopts = numoptions;
- return reloptions;
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
-
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
-
- if (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
-
- return values;
-}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * 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.
*/
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
+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);
}
- 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;
-
- for (i = 0; i < numoptions; i++)
- {
- relopt_value *optval = &options[i];
-
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
+ 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\"."));
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
+ 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);
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
- }
+ 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);
- return palloc0(size);
+ return stdrd_relopt_spec_set;
}
-/*
- * 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;
+static options_spec_set *heap_relopt_spec_set = NULL;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
+static options_spec_set *toast_relopt_spec_set = NULL;
-/*
- * Option parser for anything that uses StdRdOptions.
- */
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- 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));
+ 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;
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
-
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
-
- pfree(options);
+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 rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1949,158 +565,167 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ if (validate && relopts->validators)
+ {
+ int val_count = 0;
+ int i;
+ option_value *val_array;
- i++;
- }
+ foreach(lc, values)
+ val_count++;
+ val_array = palloc(sizeof(option_value) * val_count);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (elems)
- pfree(elems);
+ foreach(lc, relopts->validators)
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
+ pfree(val_array);
+ }
return opts;
-}
-/*
- * 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
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an options spec set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an options spec set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2110,33 +735,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.c
index 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.c
index 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.c
index d4bf0c7563..435c899110 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 */
+static 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.c
index fd1a7119b6..31a7117bf2 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.c
index fe37bc47cb..b41d36960b 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.c
index 06131f23d4..67cdefa712 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.c
index ff260c393a..a08004c045 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,23 +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.c
index a171ca8a08..8d566f08a4 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,22 +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).
@@ -1348,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/access/transam/xact.c b/src/backend/access/transam/xact.c
index 47d80b0d25..bd60b55574 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -122,7 +122,7 @@ bool bsysscan = false;
* lookups as fast as possible.
*/
static FullTransactionId XactTopFullTransactionId = {InvalidTransactionId};
-static int nParallelCurrentXids = 0;
+static int nParallelCurrentXids = 0;
static TransactionId *ParallelCurrentXids;
/*
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9abbb6b555..c91f38819d 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.c
index 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.c
index eac13ac0b7..c5bd18fa68 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;
@@ -847,7 +848,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -861,10 +862,18 @@ 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.
@@ -2039,8 +2048,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
indexInfo->ii_OpclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
attn++;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 2de0ebacec..9134af219f 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);
- break;
+ {
+ /* 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)
@@ -4102,7 +4119,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);
@@ -4144,7 +4161,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.
@@ -4365,9 +4382,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8155,12 +8177,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));
@@ -14141,12 +14164,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 */
@@ -14166,38 +14190,56 @@ 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))));
+ 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),
@@ -14207,11 +14249,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;
@@ -14286,20 +14332,23 @@ 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.c
index 00ca397fe8..608e3c181b 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
@@ -1100,11 +1099,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.c
index 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.c
index 1a64a52279..cd44d45f66 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1759,7 +1759,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;
@@ -1823,7 +1823,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 0e7b7b3138..e78ca3eb35 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1163,6 +1163,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;
@@ -1186,15 +1187,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);
@@ -1303,9 +1305,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 9e252a0891..cc3b910379 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -16,6 +16,7 @@
*/
#include "postgres.h"
+#include "access/options.h"
#include "access/reloptions.h"
#include "utils/attoptcache.h"
#include "utils/catcache.h"
@@ -148,7 +149,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 60e72f9e8b..3a2411e131 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.c
index 5609246c2f..affe4cc2b4 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/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 299b9b7621..9271b76988 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -758,7 +758,7 @@ AppendStringCommandOption(PQExpBuffer buf, bool use_new_option_syntax,
{
AppendPlainCommandOption(buf, use_new_option_syntax, option_name);
- if (option_value != NULL)
+ if (option_value !=NULL)
{
size_t length = strlen(option_value);
char *escaped_value = palloc(1 + 2 * length);
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 0b89f399f0..e4ed420179 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.h
index 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.h
index 2518660927..a7dbb8a5f8 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -14,6 +14,7 @@
#include "access/amapi.h"
#include "storage/bufpage.h"
#include "utils/typcache.h"
+#include "access/options.h"
/*
@@ -108,6 +109,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern void *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 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.h
index 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.h
index da372841c4..12ff5ffcbb 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -472,6 +471,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 93f8267b48..3979cb4e47 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1254,7 +1254,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 0000000000..1174303979
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,262 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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.h
index f740513303..1f4b6d9c28 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,40 @@
#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 */
+ 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 +67,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 +76,17 @@ extern void add_local_string_reloption(local_relopts *opts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, char *validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index 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.h
index 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.h
index 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.c
index 22578b6246..17a734e5b1 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[] =
+static 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.out
index 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.sql
index 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
--
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 4fb746930a..1dbf3cb5c7 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3103,9 +3103,9 @@ amgettuple_function
aminitparallelscan_function
aminsert_function
ammarkpos_function
-amoptions_function
amparallelrescan_function
amproperty_function
+amreloptspecset_function
amrescan_function
amrestrpos_function
amvacuumcleanup_function
@@ -3259,6 +3259,7 @@ file_type_t
filehash_hash
filehash_iterator
filemap_t
+fill_string_option
fill_string_relopt
finalize_primnode_context
find_dependent_phvs_context
@@ -3386,7 +3387,6 @@ libpq_source
line_t
lineno_t
list_sort_comparator
-local_relopt
local_relopts
local_source
locale_t
@@ -3444,6 +3444,18 @@ oidvector
on_dsm_detach_callback
on_exit_nicely_callback
openssl_tls_init_hook_typ
+opt_enum_elt_def
+option_spec_basic
+option_spec_bool
+option_spec_enum
+option_spec_int
+option_spec_real
+option_spec_string
+option_type
+option_value
+option_value_postvalidate
+option_value_status
+options_spec_set
ossl_EVP_cipher_func
other
output_type
@@ -3556,6 +3568,7 @@ pointer
polymorphic_actuals
pos_trgm
post_parse_analyze_hook_type
+postprocess_bytea_options_function
postprocess_result_function
pqbool
pqsigfunc
@@ -3610,16 +3623,6 @@ registered_buffer
regmatch_t
regoff_t
regproc
-relopt_bool
-relopt_enum
-relopt_enum_elt_def
-relopt_gen
-relopt_int
-relopt_kind
-relopt_parse_elt
-relopt_real
-relopt_string
-relopt_type
relopt_value
relopts_validator
remoteConn
@@ -3766,6 +3769,7 @@ uuid_sortsupport_state
uuid_t
va_list
vacuumingOptions
+validate_string_option
validate_string_relopt
varatt_expanded
varattrib_1b
--
2.30.2
forbid_realloc is only tested in an assert. There needs to be an "if"
test for it somewhere (suppose some extension author uses this API and
only runs it in assert-disabled environment; they'll never know they
made a mistake). But do we really need this option? Why do we need a
hardcoded limit in the number of options?
In allocateOptionsSpecSet there's a new error message with a typo
"grater" which should be "greater". But I think the message is
confusingly worded. Maybe a better wording is "the value of parameter
XXX may not be greater than YYY".
--
Álvaro Herrera Breisgau, Deutschland — https://www.EnterpriseDB.com/
В письме от среда, 18 мая 2022 г. 11:10:08 MSK пользователь Alvaro Herrera
написал:
forbid_realloc is only tested in an assert. There needs to be an "if"
test for it somewhere (suppose some extension author uses this API and
only runs it in assert-disabled environment; they'll never know they
made a mistake). But do we really need this option? Why do we need a
hardcoded limit in the number of options?
General idea was that it is better to allocate as many option_spec items as we
are going to use. It is optional, so if you do not want to allocate exact
number of options, then realloc will be used, when we adding one more item,
and all allocated items are used.
But when you explicitly specify number of items, it is better not to forget to
++ it when you add extra option in the code. That was the purpose of
forbid_realloc: to remind. It worked well for, while working with the patch
several options were added in the upstream, and this assert reminded me that I
should also allocate extra item.
If it is run in production without asserts, it is also no big deal, we will
just have another realloc.
But you are right, variable name forbid_realloc is misleading. It does not
really forbid anything, so I changed it to assert_on_realloc, so the name
tells what this flag really do.
In allocateOptionsSpecSet there's a new error message with a typo
"grater" which should be "greater". But I think the message is
confusingly worded. Maybe a better wording is "the value of parameter
XXX may not be greater than YYY".
This error message is really from bloom index. And here I was not as careful
and watchful as I should, because this error message is from the check that
was not there in original code. And this patch should not change behaviour at
all. So I removed this check completely, and will submit it later.
My original patch has a bunch of changes like that. I've removed them all, but
missed one in the contrib... :-(
Thank you for pointing to it.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
new_options_take_two_v03a.patchtext/x-patch; charset=UTF-8; name=new_options_take_two_v03a.patchDownload
From f5441cccac50d432eb5aadf0faf3bcf67a1e5988 Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Wed, 18 May 2022 18:25:46 +0300
Subject: [PATCH] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
src/backend/access/brin/brin.c | 47 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/options.c | 1364 ++++++++++
src/backend/access/common/reloptions.c | 2317 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 19 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/access/transam/xact.c | 2 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/bin/pg_basebackup/streamutil.c | 2 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 2 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 262 ++
src/include/access/reloptions.h | 205 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 148 +-
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
src/tools/pgindent/typedefs.list | 28 +-
47 files changed, 2621 insertions(+), 2379 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 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.c
index a434cf93ef..522da7dc9f 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,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* 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 +443,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/dblink/dblink.c b/contrib/dblink/dblink.c
index a561d1d652..2aafe2df83 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1977,7 +1977,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 4773cadec0..1de15ce09b 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -195,7 +195,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index e89dc74a5e..af46f0a3ee 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 52f171772d..a1eb51bcd6 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.
@@ -1785,3 +1766,29 @@ 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/Makefile
index 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.c
new file mode 100644
index 0000000000..efc377285e
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1364 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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->assert_on_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 unparsed 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 values 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 conversion.
+ */
+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/reloptions.c b/src/backend/access/common/reloptions.c
index 24211807ca..b01462d0cd 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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -489,17 +106,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -507,225 +115,9 @@ static 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,504 +152,71 @@ 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);
-}
-
-/*
- * add_local_int_reloption
- * Add a new local integer reloption
- *
- * '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;
+ optionsSpecSetAddBool(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val);
}
-
-/*
- * 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;
-}
-
-
/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
+ * add_local_int_reloption
+ * Add a new local integer reloption
+ *
+ * 'offset' is offset of int-typed field.
*/
-List *
-untransformRelOptions(Datum options)
+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)
{
- 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);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
+}
- deconstruct_array(array, TEXTOID, -1, false, TYPALIGN_INT,
- &optiondatums, NULL, &noptions);
+/*
+ * 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)
+{
+ 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,545 +253,308 @@ 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)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- 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++;
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (numoptions > 0)
- {
- reloptions = palloc(numoptions * sizeof(relopt_value));
-
- for (i = 0, j = 0; relOpts[i]; i++)
- {
- if (relOpts[i]->kinds & kind)
- {
- reloptions[j].gen = relOpts[i];
- reloptions[j].isset = false;
- j++;
- }
- }
- }
-
- /* Done if no options */
- if (PointerIsValid(DatumGetPointer(options)))
- parseRelOptionsInternal(options, validate, reloptions, numoptions);
-
- *numrelopts = numoptions;
- return reloptions;
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
-
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
-
- if (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
-
- return values;
-}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * 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.
*/
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
+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);
}
- 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;
-
- for (i = 0; i < numoptions; i++)
- {
- relopt_value *optval = &options[i];
-
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
+ 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\"."));
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
+ 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);
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
- }
+ 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);
- return palloc0(size);
+ return stdrd_relopt_spec_set;
}
-/*
- * 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;
+static options_spec_set *heap_relopt_spec_set = NULL;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
+static options_spec_set *toast_relopt_spec_set = NULL;
-/*
- * Option parser for anything that uses StdRdOptions.
- */
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- 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));
+ 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;
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
-
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
-
- pfree(options);
+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 rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1949,158 +565,167 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ if (validate && relopts->validators)
+ {
+ int val_count = 0;
+ int i;
+ option_value *val_array;
- i++;
- }
+ foreach(lc, values)
+ val_count++;
+ val_array = palloc(sizeof(option_value) * val_count);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (elems)
- pfree(elems);
+ foreach(lc, relopts->validators)
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
+ pfree(val_array);
+ }
return opts;
-}
-/*
- * 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
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an options spec set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an options spec set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2110,33 +735,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.c
index 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.c
index 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.c
index d4bf0c7563..435c899110 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 */
+static 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.c
index fd1a7119b6..31a7117bf2 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.c
index fe37bc47cb..b41d36960b 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.c
index 06131f23d4..67cdefa712 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.c
index ff260c393a..a08004c045 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,23 +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.c
index a171ca8a08..8d566f08a4 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,22 +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).
@@ -1348,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/access/transam/xact.c b/src/backend/access/transam/xact.c
index 47d80b0d25..bd60b55574 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -122,7 +122,7 @@ bool bsysscan = false;
* lookups as fast as possible.
*/
static FullTransactionId XactTopFullTransactionId = {InvalidTransactionId};
-static int nParallelCurrentXids = 0;
+static int nParallelCurrentXids = 0;
static TransactionId *ParallelCurrentXids;
/*
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9abbb6b555..c91f38819d 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.c
index 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.c
index eac13ac0b7..c5bd18fa68 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;
@@ -847,7 +848,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -861,10 +862,18 @@ 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.
@@ -2039,8 +2048,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
indexInfo->ii_OpclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
attn++;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 2de0ebacec..9134af219f 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);
- break;
+ {
+ /* 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)
@@ -4102,7 +4119,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);
@@ -4144,7 +4161,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.
@@ -4365,9 +4382,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8155,12 +8177,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));
@@ -14141,12 +14164,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 */
@@ -14166,38 +14190,56 @@ 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))));
+ 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),
@@ -14207,11 +14249,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;
@@ -14286,20 +14332,23 @@ 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.c
index 00ca397fe8..608e3c181b 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
@@ -1100,11 +1099,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.c
index 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.c
index 1a64a52279..cd44d45f66 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1759,7 +1759,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;
@@ -1823,7 +1823,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 0e7b7b3138..e78ca3eb35 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1163,6 +1163,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;
@@ -1186,15 +1187,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);
@@ -1303,9 +1305,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 9e252a0891..cc3b910379 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -16,6 +16,7 @@
*/
#include "postgres.h"
+#include "access/options.h"
#include "access/reloptions.h"
#include "utils/attoptcache.h"
#include "utils/catcache.h"
@@ -148,7 +149,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 60e72f9e8b..3a2411e131 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.c
index 5609246c2f..affe4cc2b4 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/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 299b9b7621..9271b76988 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -758,7 +758,7 @@ AppendStringCommandOption(PQExpBuffer buf, bool use_new_option_syntax,
{
AppendPlainCommandOption(buf, use_new_option_syntax, option_name);
- if (option_value != NULL)
+ if (option_value !=NULL)
{
size_t length = strlen(option_value);
char *escaped_value = palloc(1 + 2 * length);
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 0b89f399f0..e4ed420179 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.h
index 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.h
index 2518660927..a7dbb8a5f8 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -14,6 +14,7 @@
#include "access/amapi.h"
#include "storage/bufpage.h"
#include "utils/typcache.h"
+#include "access/options.h"
/*
@@ -108,6 +109,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern void *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 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.h
index 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.h
index da372841c4..12ff5ffcbb 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -472,6 +471,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 93f8267b48..3979cb4e47 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1254,7 +1254,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 0000000000..6b7a37d20c
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,262 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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.h
index f740513303..1f4b6d9c28 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,40 @@
#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 */
+ 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 +67,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 +76,17 @@ extern void add_local_string_reloption(local_relopts *opts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, char *validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index 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.h
index 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.h
index 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.c
index 22578b6246..17a734e5b1 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[] =
+static 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.out
index 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.sql
index 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
--
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 4fb746930a..1dbf3cb5c7 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3103,9 +3103,9 @@ amgettuple_function
aminitparallelscan_function
aminsert_function
ammarkpos_function
-amoptions_function
amparallelrescan_function
amproperty_function
+amreloptspecset_function
amrescan_function
amrestrpos_function
amvacuumcleanup_function
@@ -3259,6 +3259,7 @@ file_type_t
filehash_hash
filehash_iterator
filemap_t
+fill_string_option
fill_string_relopt
finalize_primnode_context
find_dependent_phvs_context
@@ -3386,7 +3387,6 @@ libpq_source
line_t
lineno_t
list_sort_comparator
-local_relopt
local_relopts
local_source
locale_t
@@ -3444,6 +3444,18 @@ oidvector
on_dsm_detach_callback
on_exit_nicely_callback
openssl_tls_init_hook_typ
+opt_enum_elt_def
+option_spec_basic
+option_spec_bool
+option_spec_enum
+option_spec_int
+option_spec_real
+option_spec_string
+option_type
+option_value
+option_value_postvalidate
+option_value_status
+options_spec_set
ossl_EVP_cipher_func
other
output_type
@@ -3556,6 +3568,7 @@ pointer
polymorphic_actuals
pos_trgm
post_parse_analyze_hook_type
+postprocess_bytea_options_function
postprocess_result_function
pqbool
pqsigfunc
@@ -3610,16 +3623,6 @@ registered_buffer
regmatch_t
regoff_t
regproc
-relopt_bool
-relopt_enum
-relopt_enum_elt_def
-relopt_gen
-relopt_int
-relopt_kind
-relopt_parse_elt
-relopt_real
-relopt_string
-relopt_type
relopt_value
relopts_validator
remoteConn
@@ -3766,6 +3769,7 @@ uuid_sortsupport_state
uuid_t
va_list
vacuumingOptions
+validate_string_option
validate_string_relopt
varatt_expanded
varattrib_1b
--
2.30.2
On Mon, 2022-02-14 at 00:43 +0300, Nikolay Shaplov wrote:
For index access methods "amoptions" member function that preformed
option
processing, were replaced with "amreloptspecset" member function that
provided
an SpecSet for reloptions for this AM, so caller can trigger option
processing
himself.
What about table access methods? There have been a couple attempts to
allow custom reloptions for table AMs. Does this patch help that use
case?
Regards,
Jeff Davis
В письме от понедельник, 11 июля 2022 г. 23:03:55 MSK пользователь Jeff Davis
написал:
For index access methods "amoptions" member function that preformed
option
processing, were replaced with "amreloptspecset" member function that
provided
an SpecSet for reloptions for this AM, so caller can trigger option
processing
himself.What about table access methods? There have been a couple attempts to
allow custom reloptions for table AMs. Does this patch help that use
case?
This patch does not add custom reloptions for table AM. It is already huge
enough, with no extra functionality. But new option engine will make adding
custom options for table AM more easy task, as main goal of this patch is to
simplify adding options everywhere they needed. And yes, adding custom table
AM options is one of my next goals, as soon as this patch is commit.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
В письме от вторник, 12 июля 2022 г. 07:30:40 MSK пользователь Nikolay Shaplov
написал:
What about table access methods? There have been a couple attempts to
allow custom reloptions for table AMs. Does this patch help that use
case?This patch does not add custom reloptions for table AM. It is already huge
enough, with no extra functionality. But new option engine will make adding
custom options for table AM more easy task, as main goal of this patch is to
simplify adding options everywhere they needed. And yes, adding custom
table AM options is one of my next goals, as soon as this patch is commit.
And here goes rebased version of the patch, that can be applied to current
master.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
new_options_take_two_v03b.patchtext/x-patch; charset=UTF-8; name=new_options_take_two_v03b.patchDownload
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Tue, 12 Jul 2022 07:43:21 +0300
Subject: [PATCH] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 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.c
index a434cf93ef..522da7dc9f 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,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* 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 +443,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/dblink/dblink.c b/contrib/dblink/dblink.c
index e323fdd0e6..b4e1efde96 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1976,7 +1976,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 4773cadec0..1de15ce09b 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -195,7 +195,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index e89dc74a5e..af46f0a3ee 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index e88f7efa7e..1b004ce08d 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -20,7 +20,6 @@
#include "access/brin_pageops.h"
#include "access/brin_xlog.h"
#include "access/relation.h"
-#include "access/reloptions.h"
#include "access/relscan.h"
#include "access/table.h"
#include "access/tableam.h"
@@ -40,7 +39,6 @@
#include "utils/memutils.h"
#include "utils/rel.h"
-
/*
* We use a BrinBuildState during initial construction of a BRIN index.
* The running state is kept in a BrinMemTuple.
@@ -119,7 +117,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = brinvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = brincostestimate;
- amroutine->amoptions = brinoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = brinvalidate;
@@ -134,6 +131,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = bringetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -962,23 +960,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.
@@ -1790,3 +1771,29 @@ 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/Makefile
index 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.c
new file mode 100644
index 0000000000..c614644b78
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1362 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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->assert_on_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_builtin(array, TEXTOID, &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_builtin(array, TEXTOID, &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 unparsed 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 values 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 conversion.
+ */
+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/reloptions.c b/src/backend/access/common/reloptions.c
index 609329bb21..b01462d0cd 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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -489,17 +106,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -507,225 +115,9 @@ static 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,502 +152,71 @@ 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);
-}
-
-/*
- * add_local_int_reloption
- * Add a new local integer reloption
- *
- * '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;
+ optionsSpecSetAddBool(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val);
}
-
-/*
- * 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_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
+ * add_local_int_reloption
+ * Add a new local integer reloption
+ *
+ * 'offset' is offset of int-typed field.
*/
-List *
-untransformRelOptions(Datum options)
+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)
{
- 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);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
+}
- deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
+/*
+ * 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)
+{
+ 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);
}
/*
@@ -1379,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,
@@ -1398,544 +253,308 @@ 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_builtin(array, TEXTOID, &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)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- 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++;
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (numoptions > 0)
- {
- reloptions = palloc(numoptions * sizeof(relopt_value));
-
- for (i = 0, j = 0; relOpts[i]; i++)
- {
- if (relOpts[i]->kinds & kind)
- {
- reloptions[j].gen = relOpts[i];
- reloptions[j].isset = false;
- j++;
- }
- }
- }
-
- /* Done if no options */
- if (PointerIsValid(DatumGetPointer(options)))
- parseRelOptionsInternal(options, validate, reloptions, numoptions);
-
- *numrelopts = numoptions;
- return reloptions;
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
-
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
-
- if (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
-
- return values;
-}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * 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.
*/
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
+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);
}
- 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;
-
- for (i = 0; i < numoptions; i++)
- {
- relopt_value *optval = &options[i];
-
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
+ 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\"."));
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
+ 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);
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
- }
+ 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);
- return palloc0(size);
+ return stdrd_relopt_spec_set;
}
-/*
- * 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;
+static options_spec_set *heap_relopt_spec_set = NULL;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
+static options_spec_set *toast_relopt_spec_set = NULL;
-/*
- * Option parser for anything that uses StdRdOptions.
- */
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- 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));
+ 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;
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
-
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
-
- pfree(options);
+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 rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1946,158 +565,167 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ if (validate && relopts->validators)
+ {
+ int val_count = 0;
+ int i;
+ option_value *val_array;
- i++;
- }
+ foreach(lc, values)
+ val_count++;
+ val_array = palloc(sizeof(option_value) * val_count);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (elems)
- pfree(elems);
+ foreach(lc, relopts->validators)
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
+ pfree(val_array);
+ }
return opts;
-}
-/*
- * 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
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an options spec set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an options spec set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2107,33 +735,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.c
index 6df7f2eaeb..6852cadcab 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -16,7 +16,7 @@
#include "access/gin_private.h"
#include "access/ginxlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/xloginsert.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
@@ -28,6 +28,7 @@
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/typcache.h"
+#include "utils/guc.h"
/*
@@ -67,7 +68,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = ginvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = ginvalidate;
@@ -82,6 +82,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -604,21 +605,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
*
@@ -705,3 +691,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.c
index 5866c6aaaf..dd710e6049 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -89,7 +89,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;
@@ -104,6 +103,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.c
index d4bf0c7563..435c899110 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 */
+static 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.c
index c361509d68..fdd8ae684d 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -86,7 +86,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;
@@ -101,6 +100,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.c
index fe37bc47cb..b41d36960b 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.c
index b52eca8f38..f7b0927f0b 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"
@@ -125,7 +126,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;
@@ -140,6 +140,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1419,3 +1420,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.c
index ff260c393a..a08004c045 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,23 +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.c
index 2c661fcf96..676cc9d4cd 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"
@@ -73,7 +73,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;
@@ -88,6 +87,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -732,22 +732,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).
@@ -1347,3 +1331,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.c
index 9abbb6b555..c91f38819d 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.c
index 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.c
index ff847579f3..3576b426e7 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"
@@ -546,7 +547,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;
@@ -862,7 +863,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -876,10 +877,18 @@ 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.
@@ -2105,8 +2114,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
indexInfo->ii_OpclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
attn++;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index ef5b34a312..46c1259dea 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);
- break;
+ {
+ /* 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)
@@ -4102,7 +4119,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);
@@ -4144,7 +4161,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.
@@ -4365,9 +4382,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8156,12 +8178,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));
@@ -14142,12 +14165,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 */
@@ -14167,38 +14191,56 @@ 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))));
+ 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),
@@ -14208,11 +14250,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;
@@ -14287,20 +14333,23 @@ 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.c
index c8bdd9992a..6eb770bfe4 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
@@ -1100,11 +1099,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.c
index 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.c
index b57253463b..41e2a1aec4 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1757,7 +1757,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
/* Add the operator class name, if non-default */
iparam->opclass = get_opclass(indclass->values[keyno], keycoltype);
iparam->opclassopts =
- untransformRelOptions(get_attoptions(source_relid, keyno + 1));
+ optionsTextArrayToDefList(get_attoptions(source_relid, keyno + 1));
iparam->ordering = SORTBY_DEFAULT;
iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
@@ -1821,7 +1821,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 6b0a865262..2f7c205ccd 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1163,6 +1163,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;
@@ -1186,15 +1187,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);
@@ -1303,9 +1305,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 9e252a0891..cc3b910379 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -16,6 +16,7 @@
*/
#include "postgres.h"
+#include "access/options.h"
#include "access/reloptions.h"
#include "utils/attoptcache.h"
#include "utils/catcache.h"
@@ -148,7 +149,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index bdb771d278..11905fb46e 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.c
index 5609246c2f..affe4cc2b4 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/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 919ec9c29c..94ce8c7ad7 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -755,7 +755,7 @@ AppendStringCommandOption(PQExpBuffer buf, bool use_new_option_syntax,
{
AppendPlainCommandOption(buf, use_new_option_syntax, option_name);
- if (option_value != NULL)
+ if (option_value !=NULL)
{
size_t length = strlen(option_value);
char *escaped_value = palloc(1 + 2 * length);
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 1dc674d230..afb619f8c4 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -136,10 +136,6 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
double *indexCorrelation,
double *indexPages);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
-
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -186,6 +182,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* get catalog of reloptions definitions */
+typedef void *(*amreloptspecset_function) ();
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -263,7 +262,6 @@ typedef struct IndexAmRoutine
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
- amoptions_function amoptions;
amproperty_function amproperty; /* can be NULL */
ambuildphasename_function ambuildphasename; /* can be NULL */
amvalidate_function amvalidate;
@@ -275,6 +273,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amreloptspecset_function amreloptspecset; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 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.h
index 2518660927..a7dbb8a5f8 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -14,6 +14,7 @@
#include "access/amapi.h"
#include "storage/bufpage.h"
#include "utils/typcache.h"
+#include "access/options.h"
/*
@@ -108,6 +109,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern void *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 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.h
index 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.h
index da372841c4..12ff5ffcbb 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -472,6 +471,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 93f8267b48..3979cb4e47 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1254,7 +1254,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 0000000000..6b7a37d20c
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,262 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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.h
index f740513303..1f4b6d9c28 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,40 @@
#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 */
+ 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 +67,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 +76,17 @@ extern void add_local_string_reloption(local_relopts *opts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, char *validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index 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.h
index 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.h
index 0c48654b96..6f6fd32714 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -34,7 +34,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index a0894ff986..d715c7f2c1 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[] =
+static 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.
*/
@@ -308,7 +294,6 @@ dihandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = divacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = dicostestimate;
- amroutine->amoptions = dioptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = divalidate;
@@ -322,12 +307,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.out
index 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.sql
index 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
--
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 34a76ceb60..fcd1b3dcfa 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3105,9 +3105,9 @@ amgettuple_function
aminitparallelscan_function
aminsert_function
ammarkpos_function
-amoptions_function
amparallelrescan_function
amproperty_function
+amreloptspecset_function
amrescan_function
amrestrpos_function
amvacuumcleanup_function
@@ -3261,6 +3261,7 @@ file_type_t
filehash_hash
filehash_iterator
filemap_t
+fill_string_option
fill_string_relopt
finalize_primnode_context
find_dependent_phvs_context
@@ -3388,7 +3389,6 @@ libpq_source
line_t
lineno_t
list_sort_comparator
-local_relopt
local_relopts
local_source
locale_t
@@ -3446,6 +3446,18 @@ oidvector
on_dsm_detach_callback
on_exit_nicely_callback
openssl_tls_init_hook_typ
+opt_enum_elt_def
+option_spec_basic
+option_spec_bool
+option_spec_enum
+option_spec_int
+option_spec_real
+option_spec_string
+option_type
+option_value
+option_value_postvalidate
+option_value_status
+options_spec_set
ossl_EVP_cipher_func
other
output_type
@@ -3558,6 +3570,7 @@ pointer
polymorphic_actuals
pos_trgm
post_parse_analyze_hook_type
+postprocess_bytea_options_function
postprocess_result_function
pqbool
pqsigfunc
@@ -3612,16 +3625,6 @@ registered_buffer
regmatch_t
regoff_t
regproc
-relopt_bool
-relopt_enum
-relopt_enum_elt_def
-relopt_gen
-relopt_int
-relopt_kind
-relopt_parse_elt
-relopt_real
-relopt_string
-relopt_type
relopt_value
relopts_validator
remoteConn
@@ -3768,6 +3771,7 @@ uuid_sortsupport_state
uuid_t
va_list
vacuumingOptions
+validate_string_option
validate_string_relopt
varatt_expanded
varattrib_1b
2022年7月12日(火) 13:47 Nikolay Shaplov <dhyan@nataraj.su>:
В письме от вторник, 12 июля 2022 г. 07:30:40 MSK пользователь Nikolay Shaplov
написал:What about table access methods? There have been a couple attempts to
allow custom reloptions for table AMs. Does this patch help that use
case?This patch does not add custom reloptions for table AM. It is already huge
enough, with no extra functionality. But new option engine will make adding
custom options for table AM more easy task, as main goal of this patch is to
simplify adding options everywhere they needed. And yes, adding custom
table AM options is one of my next goals, as soon as this patch is commit.And here goes rebased version of the patch, that can be applied to current
master.
Hi
cfbot reports the patch no longer applies. As CommitFest 2022-11 is
currently underway, this would be an excellent time to update the patch.
Thanks
Ian Barwick
В письме от пятница, 4 ноября 2022 г. 03:47:09 MSK пользователь Ian Lawrence
Barwick написал:
cfbot reports the patch no longer applies. As CommitFest 2022-11 is
currently underway, this would be an excellent time to update the patch.
Oups! I should have done it before...
Fixed
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
new_options_take_two_v03c.patchtext/x-patch; charset=UTF-8; name=new_options_take_two_v03c.patchDownload
From 410f445399c7020e4829624c6d152964bfc74bd2 Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Fri, 4 Nov 2022 19:58:38 +0300
Subject: [PATCH 1/1] New options engine
Add new options engine that will allow to define options
directly in any part of code they are needed (anywhere, not only for relation
and op. classes) and use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
src/backend/access/brin/brin.c | 47 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/options.c | 1362 ++++++++++
src/backend/access/common/reloptions.c | 2314 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 19 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/bin/pg_basebackup/streamutil.c | 2 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 2 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 262 ++
src/include/access/reloptions.h | 205 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 148 +-
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
src/tools/pgindent/typedefs.list | 28 +-
46 files changed, 2619 insertions(+), 2374 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 6d58e22813..ce7e5a22d1 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"
@@ -206,7 +207,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern void *blrelopt_specset(void);
+extern void blReloptionPostprocess(void *, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index a434cf93ef..522da7dc9f 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,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* 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 +443,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/dblink/dblink.c b/contrib/dblink/dblink.c
index 9202c35847..6004287062 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1976,7 +1976,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 67821cd25b..b3ceb1580a 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -196,7 +196,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index fa80ee2a55..42848b35e1 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -71,7 +71,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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index e89dc74a5e..af46f0a3ee 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 20b7d65b94..72b2c37e3e 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"
@@ -41,7 +40,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);
}
@@ -962,23 +960,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.
@@ -1790,3 +1771,29 @@ 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/Makefile
index 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.c
new file mode 100644
index 0000000000..c614644b78
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1362 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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->assert_on_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_builtin(array, TEXTOID, &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_builtin(array, TEXTOID, &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 unparsed 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 values 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 conversion.
+ */
+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/reloptions.c b/src/backend/access/common/reloptions.c
index 6458a9c276..d7200e4b86 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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -489,17 +106,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -507,225 +115,9 @@ static 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 *relopts, Size relopt_struct_size)
{
- relopts->options = NIL;
relopts->validators = NIL;
- relopts->relopt_struct_size = relopt_struct_size;
+ relopts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, true, 0);
}
/*
@@ -751,112 +142,6 @@ register_reloptions_validator(local_relopts *relopts, relopts_validator validato
relopts->validators = lappend(relopts->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,502 +152,71 @@ 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);
-}
-
-/*
- * add_local_int_reloption
- * Add a new local integer reloption
- *
- * '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;
+ optionsSpecSetAddBool(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val);
}
-
-/*
- * 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_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
+ * add_local_int_reloption
+ * Add a new local integer reloption
+ *
+ * 'offset' is offset of int-typed field.
*/
-List *
-untransformRelOptions(Datum options)
+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)
{
- 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);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
+}
- deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
+/*
+ * 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)
+{
+ 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(p);
- }
- result = lappend(result, makeDefElem(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);
}
/*
@@ -1379,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,
@@ -1398,544 +253,308 @@ 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_builtin(array, TEXTOID, &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)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- 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++;
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (numoptions > 0)
- {
- reloptions = palloc(numoptions * sizeof(relopt_value));
-
- for (i = 0, j = 0; relOpts[i]; i++)
- {
- if (relOpts[i]->kinds & kind)
- {
- reloptions[j].gen = relOpts[i];
- reloptions[j].isset = false;
- j++;
- }
- }
- }
-
- /* Done if no options */
- if (PointerIsValid(DatumGetPointer(options)))
- parseRelOptionsInternal(options, validate, reloptions, numoptions);
-
- *numrelopts = numoptions;
- return reloptions;
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
-
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
-
- if (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
-
- return values;
-}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * 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.
*/
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
+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);
}
- 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;
-
- for (i = 0; i < numoptions; i++)
- {
- relopt_value *optval = &options[i];
-
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
+ 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\"."));
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
+ 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);
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
- }
+ 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);
- return palloc0(size);
+ return stdrd_relopt_spec_set;
}
-/*
- * 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;
+static options_spec_set *heap_relopt_spec_set = NULL;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
+static options_spec_set *toast_relopt_spec_set = NULL;
-/*
- * Option parser for anything that uses StdRdOptions.
- */
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- 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));
+ 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;
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
-
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
-
- pfree(options);
+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 rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1946,158 +565,167 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ if (validate && relopts->validators)
+ {
+ int val_count = 0;
+ int i;
+ option_value *val_array;
- i++;
- }
+ foreach(lc, values)
+ val_count++;
+ val_array = palloc(sizeof(option_value) * val_count);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (elems)
- pfree(elems);
+ foreach(lc, relopts->validators)
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
+ pfree(val_array);
+ }
return opts;
-}
-/*
- * 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
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an options spec set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an options spec set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2107,33 +735,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.c
index 6df7f2eaeb..6852cadcab 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -16,7 +16,7 @@
#include "access/gin_private.h"
#include "access/ginxlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/xloginsert.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
@@ -28,6 +28,7 @@
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/typcache.h"
+#include "utils/guc.h"
/*
@@ -67,7 +68,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = ginvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = ginvalidate;
@@ -82,6 +82,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -604,21 +605,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
*
@@ -705,3 +691,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.c
index 30069f139c..bdb81dc8e9 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -89,7 +89,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;
@@ -104,6 +103,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.c
index 1532462317..624bf965b0 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 */
+static 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.c
index c361509d68..fdd8ae684d 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -86,7 +86,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;
@@ -101,6 +100,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.c
index 32822dbb6b..c745c7316a 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.c
index b52eca8f38..f7b0927f0b 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"
@@ -125,7 +126,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;
@@ -140,6 +140,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1419,3 +1420,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.c
index ff260c393a..a08004c045 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,23 +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.c
index 2c661fcf96..676cc9d4cd 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"
@@ -73,7 +73,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;
@@ -88,6 +87,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -732,22 +732,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).
@@ -1347,3 +1331,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.c
index 152c29b551..6e9d0f2460 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.c
index 91f4dd30de..da4d5da896 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -112,7 +112,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 659e189549..6071e493d3 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"
@@ -547,7 +548,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;
@@ -862,7 +863,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -876,10 +877,18 @@ 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.
@@ -2116,8 +2125,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
palloc0_array(Datum, indexInfo->ii_NumIndexAttrs);
indexInfo->ii_OpclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
attn++;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6007e10730..d5e8630086 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"
@@ -676,7 +677,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;
@@ -824,19 +824,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);
- break;
+ {
+ /* 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)
@@ -4120,7 +4137,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);
@@ -4162,7 +4179,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.
@@ -4383,9 +4400,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8194,12 +8216,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));
@@ -14178,12 +14201,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 */
@@ -14203,38 +14227,56 @@ 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))));
+ 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),
@@ -14244,11 +14286,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;
@@ -14323,20 +14369,23 @@ 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.c
index 45b30ca566..1f76ae9790 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -339,10 +339,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
@@ -1058,11 +1057,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.c
index 56fcb8edf1..fabb70e8bd 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -80,7 +80,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
if (isnull)
fdw->options = NIL;
else
- fdw->options = untransformRelOptions(datum);
+ fdw->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -167,7 +167,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
if (isnull)
server->options = NIL;
else
- server->options = untransformRelOptions(datum);
+ server->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -235,7 +235,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -272,7 +272,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -305,7 +305,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -513,7 +513,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 */
@@ -610,7 +610,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 8140e79d8f..0bbb12b846 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1767,7 +1767,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;
@@ -1831,7 +1831,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 247d0816ad..54a0536307 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1163,6 +1163,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;
@@ -1186,15 +1187,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);
@@ -1303,9 +1305,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 9e252a0891..cc3b910379 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -16,6 +16,7 @@
*/
#include "postgres.h"
+#include "access/options.h"
#include "access/reloptions.h"
#include "utils/attoptcache.h"
#include "utils/catcache.h"
@@ -148,7 +149,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index bd6cd4e47b..71e1ac5765 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -462,7 +462,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -477,11 +477,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;
@@ -492,7 +492,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index 5609246c2f..affe4cc2b4 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/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 4ccb90c197..7643bd34a0 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -755,7 +755,7 @@ AppendStringCommandOption(PQExpBuffer buf, bool use_new_option_syntax,
{
AppendPlainCommandOption(buf, use_new_option_syntax, option_name);
- if (option_value != NULL)
+ if (option_value !=NULL)
{
size_t length = strlen(option_value);
char *escaped_value = palloc(1 + 2 * length);
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 1dc674d230..afb619f8c4 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -136,10 +136,6 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
double *indexCorrelation,
double *indexPages);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
-
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -186,6 +182,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* get catalog of reloptions definitions */
+typedef void *(*amreloptspecset_function) ();
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -263,7 +262,6 @@ typedef struct IndexAmRoutine
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
- amoptions_function amoptions;
amproperty_function amproperty; /* can be NULL */
ambuildphasename_function ambuildphasename; /* can be NULL */
amvalidate_function amvalidate;
@@ -275,6 +273,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amreloptspecset_function amreloptspecset; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 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.h
index 2518660927..a7dbb8a5f8 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -14,6 +14,7 @@
#include "access/amapi.h"
#include "storage/bufpage.h"
#include "utils/typcache.h"
+#include "access/options.h"
/*
@@ -108,6 +109,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern void *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 145ec7743c..4a5a87d002 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -108,6 +108,7 @@ extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
+extern void *gingetreloptspecset(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 093bf23443..5f1a2dbd92 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.h
index da372841c4..12ff5ffcbb 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -472,6 +471,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 8e4f6864e5..e40566b238 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1253,7 +1253,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 0000000000..6b7a37d20c
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,262 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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.h
index 21bde78ed0..93acc02cf7 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,40 @@
#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 */
+ 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 *relopts, Size relopt_struct_size);
extern void register_reloptions_validator(local_relopts *relopts,
@@ -210,7 +67,7 @@ extern void add_local_real_reloption(local_relopts *relopts, 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 *relopts, const char *name,
@@ -219,29 +76,17 @@ extern void add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, char *validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index 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.h
index 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.h
index 03f14d6be1..493fa0ce5d 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -34,7 +34,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index 67c30394c8..d715c7f2c1 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"
@@ -23,11 +23,7 @@
PG_MODULE_MAGIC;
-/* parse table for fillRelOptions */
-relopt_parse_elt di_relopt_tab[6];
-
-/* Kind of relation options for dummy index */
-relopt_kind di_relopt_kind;
+void _PG_init(void);
typedef enum DummyAmEnum
{
@@ -47,7 +43,7 @@ typedef struct DummyIndexOptions
int option_string_null_offset;
} DummyIndexOptions;
-relopt_enum_elt_def dummyAmEnumValues[] =
+static opt_enum_elt_def dummyAmEnumValues[] =
{
{"one", DUMMY_AM_ENUM_ONE},
{"two", DUMMY_AM_ENUM_TWO},
@@ -61,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.
*/
@@ -216,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.
*/
@@ -306,7 +294,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;
@@ -320,12 +307,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.out
index 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.sql
index 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
--
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 9683b0a88e..ed0db93d81 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3099,9 +3099,9 @@ amgettuple_function
aminitparallelscan_function
aminsert_function
ammarkpos_function
-amoptions_function
amparallelrescan_function
amproperty_function
+amreloptspecset_function
amrescan_function
amrestrpos_function
amvacuumcleanup_function
@@ -3257,6 +3257,7 @@ file_type_t
filehash_hash
filehash_iterator
filemap_t
+fill_string_option
fill_string_relopt
finalize_primnode_context
find_dependent_phvs_context
@@ -3384,7 +3385,6 @@ libpq_source
line_t
lineno_t
list_sort_comparator
-local_relopt
local_relopts
local_source
locale_t
@@ -3442,6 +3442,18 @@ oidvector
on_dsm_detach_callback
on_exit_nicely_callback
openssl_tls_init_hook_typ
+opt_enum_elt_def
+option_spec_basic
+option_spec_bool
+option_spec_enum
+option_spec_int
+option_spec_real
+option_spec_string
+option_type
+option_value
+option_value_postvalidate
+option_value_status
+options_spec_set
ossl_EVP_cipher_func
other
output_type
@@ -3553,6 +3565,7 @@ pointer
polymorphic_actuals
pos_trgm
post_parse_analyze_hook_type
+postprocess_bytea_options_function
postprocess_result_function
pqbool
pqsigfunc
@@ -3608,16 +3621,6 @@ registered_buffer
regmatch_t
regoff_t
regproc
-relopt_bool
-relopt_enum
-relopt_enum_elt_def
-relopt_gen
-relopt_int
-relopt_kind
-relopt_parse_elt
-relopt_real
-relopt_string
-relopt_type
relopt_value
relopts_validator
remoteConn
@@ -3764,6 +3767,7 @@ uuid_sortsupport_state
uuid_t
va_list
vacuumingOptions
+validate_string_option
validate_string_relopt
varatt_expanded
varattrib_1b
--
2.30.2
В письме от пятница, 4 ноября 2022 г. 21:06:38 MSK пользователь Nikolay
Shaplov написал:
В письме от пятница, 4 ноября 2022 г. 03:47:09 MSK пользователь Ian Lawrence
Barwick написал:cfbot reports the patch no longer applies. As CommitFest 2022-11 is
currently underway, this would be an excellent time to update the patch.Oups! I should have done it before...
Fixed
Trying to fix meson build
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
new_options_take_two_v03d.patchtext/x-patch; charset=UTF-8; name=new_options_take_two_v03d.patchDownload
From d96186f5be665ba3569a01253dbe50f1063ab0df Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Fri, 4 Nov 2022 22:27:04 +0300
Subject: [PATCH 1/1] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
src/backend/access/brin/brin.c | 47 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/meson.build | 1 +
src/backend/access/common/options.c | 1362 ++++++++++
src/backend/access/common/reloptions.c | 2314 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 19 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/bin/pg_basebackup/streamutil.c | 2 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 2 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 262 ++
src/include/access/reloptions.h | 205 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 148 +-
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
src/tools/pgindent/typedefs.list | 28 +-
47 files changed, 2620 insertions(+), 2374 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 6d58e22813..ce7e5a22d1 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"
@@ -206,7 +207,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern void *blrelopt_specset(void);
+extern void blReloptionPostprocess(void *, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index a434cf93ef..522da7dc9f 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,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* 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 +443,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/dblink/dblink.c b/contrib/dblink/dblink.c
index 9202c35847..6004287062 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1976,7 +1976,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 67821cd25b..b3ceb1580a 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -196,7 +196,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index fa80ee2a55..42848b35e1 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -71,7 +71,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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index e89dc74a5e..af46f0a3ee 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 20b7d65b94..72b2c37e3e 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"
@@ -41,7 +40,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);
}
@@ -962,23 +960,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.
@@ -1790,3 +1771,29 @@ 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/Makefile
index 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/meson.build b/src/backend/access/common/meson.build
index 857beaa32d..fe8c1274a4 100644
--- a/src/backend/access/common/meson.build
+++ b/src/backend/access/common/meson.build
@@ -4,6 +4,7 @@ backend_sources += files(
'detoast.c',
'heaptuple.c',
'indextuple.c',
+ 'options.c',
'printsimple.c',
'printtup.c',
'relation.c',
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 0000000000..c614644b78
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1362 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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->assert_on_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_builtin(array, TEXTOID, &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_builtin(array, TEXTOID, &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 unparsed 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 values 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 conversion.
+ */
+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/reloptions.c b/src/backend/access/common/reloptions.c
index 6458a9c276..d7200e4b86 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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -489,17 +106,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -507,225 +115,9 @@ static 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 *relopts, Size relopt_struct_size)
{
- relopts->options = NIL;
relopts->validators = NIL;
- relopts->relopt_struct_size = relopt_struct_size;
+ relopts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, true, 0);
}
/*
@@ -751,112 +142,6 @@ register_reloptions_validator(local_relopts *relopts, relopts_validator validato
relopts->validators = lappend(relopts->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,502 +152,71 @@ 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);
-}
-
-/*
- * add_local_int_reloption
- * Add a new local integer reloption
- *
- * '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;
+ optionsSpecSetAddBool(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val);
}
-
-/*
- * 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_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
+ * add_local_int_reloption
+ * Add a new local integer reloption
+ *
+ * 'offset' is offset of int-typed field.
*/
-List *
-untransformRelOptions(Datum options)
+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)
{
- 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);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
+}
- deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
+/*
+ * 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)
+{
+ 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(p);
- }
- result = lappend(result, makeDefElem(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);
}
/*
@@ -1379,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,
@@ -1398,544 +253,308 @@ 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_builtin(array, TEXTOID, &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)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- 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++;
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (numoptions > 0)
- {
- reloptions = palloc(numoptions * sizeof(relopt_value));
-
- for (i = 0, j = 0; relOpts[i]; i++)
- {
- if (relOpts[i]->kinds & kind)
- {
- reloptions[j].gen = relOpts[i];
- reloptions[j].isset = false;
- j++;
- }
- }
- }
-
- /* Done if no options */
- if (PointerIsValid(DatumGetPointer(options)))
- parseRelOptionsInternal(options, validate, reloptions, numoptions);
-
- *numrelopts = numoptions;
- return reloptions;
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
-
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
-
- if (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
-
- return values;
-}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * 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.
*/
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
+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);
}
- 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;
-
- for (i = 0; i < numoptions; i++)
- {
- relopt_value *optval = &options[i];
-
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
+ 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\"."));
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
+ 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);
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
- }
+ 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);
- return palloc0(size);
+ return stdrd_relopt_spec_set;
}
-/*
- * 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;
+static options_spec_set *heap_relopt_spec_set = NULL;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
+static options_spec_set *toast_relopt_spec_set = NULL;
-/*
- * Option parser for anything that uses StdRdOptions.
- */
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- 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));
+ 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;
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
-
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
-
- pfree(options);
+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 rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1946,158 +565,167 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ if (validate && relopts->validators)
+ {
+ int val_count = 0;
+ int i;
+ option_value *val_array;
- i++;
- }
+ foreach(lc, values)
+ val_count++;
+ val_array = palloc(sizeof(option_value) * val_count);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (elems)
- pfree(elems);
+ foreach(lc, relopts->validators)
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
+ pfree(val_array);
+ }
return opts;
-}
-/*
- * 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
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an options spec set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an options spec set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2107,33 +735,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.c
index 6df7f2eaeb..6852cadcab 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -16,7 +16,7 @@
#include "access/gin_private.h"
#include "access/ginxlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/xloginsert.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
@@ -28,6 +28,7 @@
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/typcache.h"
+#include "utils/guc.h"
/*
@@ -67,7 +68,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = ginvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = ginvalidate;
@@ -82,6 +82,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -604,21 +605,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
*
@@ -705,3 +691,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.c
index 30069f139c..bdb81dc8e9 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -89,7 +89,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;
@@ -104,6 +103,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.c
index 1532462317..624bf965b0 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 */
+static 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.c
index c361509d68..fdd8ae684d 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -86,7 +86,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;
@@ -101,6 +100,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.c
index 32822dbb6b..c745c7316a 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.c
index b52eca8f38..f7b0927f0b 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"
@@ -125,7 +126,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;
@@ -140,6 +140,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1419,3 +1420,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.c
index ff260c393a..a08004c045 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,23 +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.c
index 2c661fcf96..676cc9d4cd 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"
@@ -73,7 +73,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;
@@ -88,6 +87,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -732,22 +732,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).
@@ -1347,3 +1331,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.c
index 152c29b551..6e9d0f2460 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.c
index 91f4dd30de..da4d5da896 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -112,7 +112,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 659e189549..6071e493d3 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"
@@ -547,7 +548,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;
@@ -862,7 +863,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -876,10 +877,18 @@ 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.
@@ -2116,8 +2125,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
palloc0_array(Datum, indexInfo->ii_NumIndexAttrs);
indexInfo->ii_OpclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
attn++;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6007e10730..d5e8630086 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"
@@ -676,7 +677,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;
@@ -824,19 +824,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);
- break;
+ {
+ /* 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)
@@ -4120,7 +4137,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);
@@ -4162,7 +4179,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.
@@ -4383,9 +4400,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8194,12 +8216,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));
@@ -14178,12 +14201,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 */
@@ -14203,38 +14227,56 @@ 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))));
+ 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),
@@ -14244,11 +14286,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;
@@ -14323,20 +14369,23 @@ 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.c
index 45b30ca566..1f76ae9790 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -339,10 +339,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
@@ -1058,11 +1057,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.c
index 56fcb8edf1..fabb70e8bd 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -80,7 +80,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
if (isnull)
fdw->options = NIL;
else
- fdw->options = untransformRelOptions(datum);
+ fdw->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -167,7 +167,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
if (isnull)
server->options = NIL;
else
- server->options = untransformRelOptions(datum);
+ server->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -235,7 +235,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -272,7 +272,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -305,7 +305,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -513,7 +513,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 */
@@ -610,7 +610,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 8140e79d8f..0bbb12b846 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1767,7 +1767,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;
@@ -1831,7 +1831,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 247d0816ad..54a0536307 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1163,6 +1163,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;
@@ -1186,15 +1187,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);
@@ -1303,9 +1305,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 9e252a0891..cc3b910379 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -16,6 +16,7 @@
*/
#include "postgres.h"
+#include "access/options.h"
#include "access/reloptions.h"
#include "utils/attoptcache.h"
#include "utils/catcache.h"
@@ -148,7 +149,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index bd6cd4e47b..71e1ac5765 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -462,7 +462,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -477,11 +477,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;
@@ -492,7 +492,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index 5609246c2f..affe4cc2b4 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/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 4ccb90c197..7643bd34a0 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -755,7 +755,7 @@ AppendStringCommandOption(PQExpBuffer buf, bool use_new_option_syntax,
{
AppendPlainCommandOption(buf, use_new_option_syntax, option_name);
- if (option_value != NULL)
+ if (option_value !=NULL)
{
size_t length = strlen(option_value);
char *escaped_value = palloc(1 + 2 * length);
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 1dc674d230..afb619f8c4 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -136,10 +136,6 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
double *indexCorrelation,
double *indexPages);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
-
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -186,6 +182,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* get catalog of reloptions definitions */
+typedef void *(*amreloptspecset_function) ();
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -263,7 +262,6 @@ typedef struct IndexAmRoutine
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
- amoptions_function amoptions;
amproperty_function amproperty; /* can be NULL */
ambuildphasename_function ambuildphasename; /* can be NULL */
amvalidate_function amvalidate;
@@ -275,6 +273,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amreloptspecset_function amreloptspecset; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 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.h
index 2518660927..a7dbb8a5f8 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -14,6 +14,7 @@
#include "access/amapi.h"
#include "storage/bufpage.h"
#include "utils/typcache.h"
+#include "access/options.h"
/*
@@ -108,6 +109,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern void *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 145ec7743c..4a5a87d002 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -108,6 +108,7 @@ extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
+extern void *gingetreloptspecset(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 093bf23443..5f1a2dbd92 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.h
index da372841c4..12ff5ffcbb 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -472,6 +471,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 8e4f6864e5..e40566b238 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1253,7 +1253,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 0000000000..6b7a37d20c
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,262 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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.h
index 21bde78ed0..93acc02cf7 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,40 @@
#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 */
+ 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 *relopts, Size relopt_struct_size);
extern void register_reloptions_validator(local_relopts *relopts,
@@ -210,7 +67,7 @@ extern void add_local_real_reloption(local_relopts *relopts, 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 *relopts, const char *name,
@@ -219,29 +76,17 @@ extern void add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, char *validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index 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.h
index 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.h
index 03f14d6be1..493fa0ce5d 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -34,7 +34,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index 67c30394c8..d715c7f2c1 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"
@@ -23,11 +23,7 @@
PG_MODULE_MAGIC;
-/* parse table for fillRelOptions */
-relopt_parse_elt di_relopt_tab[6];
-
-/* Kind of relation options for dummy index */
-relopt_kind di_relopt_kind;
+void _PG_init(void);
typedef enum DummyAmEnum
{
@@ -47,7 +43,7 @@ typedef struct DummyIndexOptions
int option_string_null_offset;
} DummyIndexOptions;
-relopt_enum_elt_def dummyAmEnumValues[] =
+static opt_enum_elt_def dummyAmEnumValues[] =
{
{"one", DUMMY_AM_ENUM_ONE},
{"two", DUMMY_AM_ENUM_TWO},
@@ -61,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.
*/
@@ -216,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.
*/
@@ -306,7 +294,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;
@@ -320,12 +307,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.out
index 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.sql
index 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
--
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 9683b0a88e..ed0db93d81 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3099,9 +3099,9 @@ amgettuple_function
aminitparallelscan_function
aminsert_function
ammarkpos_function
-amoptions_function
amparallelrescan_function
amproperty_function
+amreloptspecset_function
amrescan_function
amrestrpos_function
amvacuumcleanup_function
@@ -3257,6 +3257,7 @@ file_type_t
filehash_hash
filehash_iterator
filemap_t
+fill_string_option
fill_string_relopt
finalize_primnode_context
find_dependent_phvs_context
@@ -3384,7 +3385,6 @@ libpq_source
line_t
lineno_t
list_sort_comparator
-local_relopt
local_relopts
local_source
locale_t
@@ -3442,6 +3442,18 @@ oidvector
on_dsm_detach_callback
on_exit_nicely_callback
openssl_tls_init_hook_typ
+opt_enum_elt_def
+option_spec_basic
+option_spec_bool
+option_spec_enum
+option_spec_int
+option_spec_real
+option_spec_string
+option_type
+option_value
+option_value_postvalidate
+option_value_status
+options_spec_set
ossl_EVP_cipher_func
other
output_type
@@ -3553,6 +3565,7 @@ pointer
polymorphic_actuals
pos_trgm
post_parse_analyze_hook_type
+postprocess_bytea_options_function
postprocess_result_function
pqbool
pqsigfunc
@@ -3608,16 +3621,6 @@ registered_buffer
regmatch_t
regoff_t
regproc
-relopt_bool
-relopt_enum
-relopt_enum_elt_def
-relopt_gen
-relopt_int
-relopt_kind
-relopt_parse_elt
-relopt_real
-relopt_string
-relopt_type
relopt_value
relopts_validator
remoteConn
@@ -3764,6 +3767,7 @@ uuid_sortsupport_state
uuid_t
va_list
vacuumingOptions
+validate_string_option
validate_string_relopt
varatt_expanded
varattrib_1b
--
2.30.2
В письме от пятница, 4 ноября 2022 г. 22:30:06 MSK пользователь Nikolay
Shaplov написал:
cfbot reports the patch no longer applies. As CommitFest 2022-11 is
currently underway, this would be an excellent time to update the patch.Oups! I should have done it before...
FixedTrying to fix meson build
Trying to fix compiler warnings.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
new_options_take_two_v03e.patchtext/x-patch; charset=UTF-8; name=new_options_take_two_v03e.patchDownload
From 1b0fc5e2f593db815f8d22be2b655d121e8e0799 Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Sun, 6 Nov 2022 19:19:13 +0300
Subject: [PATCH 03e] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
src/backend/access/brin/brin.c | 47 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/meson.build | 1 +
src/backend/access/common/options.c | 1362 ++++++++++
src/backend/access/common/reloptions.c | 2314 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 19 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/bin/pg_basebackup/streamutil.c | 2 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 2 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 262 ++
src/include/access/reloptions.h | 205 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 148 +-
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
src/tools/pgindent/typedefs.list | 28 +-
47 files changed, 2620 insertions(+), 2374 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 6d58e22813..ce7e5a22d1 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"
@@ -206,7 +207,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern void *blrelopt_specset(void);
+extern void blReloptionPostprocess(void *, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index a434cf93ef..522da7dc9f 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,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* 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 +443,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/dblink/dblink.c b/contrib/dblink/dblink.c
index 9202c35847..6004287062 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1976,7 +1976,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 67821cd25b..b3ceb1580a 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -196,7 +196,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index fa80ee2a55..42848b35e1 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -71,7 +71,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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index e89dc74a5e..af46f0a3ee 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 20b7d65b94..72b2c37e3e 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"
@@ -41,7 +40,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);
}
@@ -962,23 +960,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.
@@ -1790,3 +1771,29 @@ 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/Makefile
index 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/meson.build b/src/backend/access/common/meson.build
index 857beaa32d..fe8c1274a4 100644
--- a/src/backend/access/common/meson.build
+++ b/src/backend/access/common/meson.build
@@ -4,6 +4,7 @@ backend_sources += files(
'detoast.c',
'heaptuple.c',
'indextuple.c',
+ 'options.c',
'printsimple.c',
'printtup.c',
'relation.c',
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 0000000000..c7aab3ab33
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1362 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 *namspace, 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 (namspace)
+ {
+ spec_set->namspace = palloc(strlen(namspace) + 1);
+ strcpy(spec_set->namspace, namspace);
+ }
+ else
+ spec_set->namspace = NULL;
+ if (num_items_expected > 0)
+ {
+ spec_set->num_allocated = num_items_expected;
+ spec_set->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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->assert_on_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->namspace = palloc(strlen(def->defnamespace) + 1);
+ strcpy(option_dst->namspace, def->defnamespace);
+ }
+ else
+ option_dst->namspace = 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_builtin(array, TEXTOID, &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 j;
+ 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 (j = 0; j < text_len; j = j + pg_mblen(text_str))
+ {
+ if (text_str[j] == '=')
+ {
+ name_len = j;
+ 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->namspace = 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_namspaces array. Items without namspace are also accepted
+ *
+ * Used while validation of syntax parser output. Error is thrown if unproper
+ * namespace is found.
+ */
+void
+optionsDefListValdateNamespaces(List *defList, char **allowed_namspaces)
+{
+ 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_namspaces[i])
+ {
+ if (strcmp(def->defnamespace,
+ allowed_namspaces[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 "namspace" namespace. If "namspace"
+ * is NULL, only namespaseless options are returned
+ */
+List *
+optionsDefListFilterNamespaces(List *defList, const char *namspace)
+{
+ ListCell *cell;
+ List *result = NIL;
+
+ foreach(cell, defList)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ if ((!namspace && !def->defnamespace) ||
+ (namspace && def->defnamespace &&
+ strcmp(namspace, 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_builtin(array, TEXTOID, &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 unparsed 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 values 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->namspace && (!spec_set->namspace ||
+ strcmp(spec_set->namspace, option->namspace) != 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter namespace \"%s\"",
+ option->namspace)));
+
+ 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->namspace)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s.%s\"",
+ option->namspace, 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 conversion.
+ */
+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/reloptions.c b/src/backend/access/common/reloptions.c
index 6458a9c276..d7200e4b86 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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -489,17 +106,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -507,225 +115,9 @@ static 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 *relopts, Size relopt_struct_size)
{
- relopts->options = NIL;
relopts->validators = NIL;
- relopts->relopt_struct_size = relopt_struct_size;
+ relopts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, true, 0);
}
/*
@@ -751,112 +142,6 @@ register_reloptions_validator(local_relopts *relopts, relopts_validator validato
relopts->validators = lappend(relopts->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,502 +152,71 @@ 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);
-}
-
-/*
- * add_local_int_reloption
- * Add a new local integer reloption
- *
- * '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;
+ optionsSpecSetAddBool(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val);
}
-
-/*
- * 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_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
+ * add_local_int_reloption
+ * Add a new local integer reloption
+ *
+ * 'offset' is offset of int-typed field.
*/
-List *
-untransformRelOptions(Datum options)
+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)
{
- 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);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
+}
- deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
+/*
+ * 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)
+{
+ 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(p);
- }
- result = lappend(result, makeDefElem(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);
}
/*
@@ -1379,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,
@@ -1398,544 +253,308 @@ 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_builtin(array, TEXTOID, &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)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- 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++;
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (numoptions > 0)
- {
- reloptions = palloc(numoptions * sizeof(relopt_value));
-
- for (i = 0, j = 0; relOpts[i]; i++)
- {
- if (relOpts[i]->kinds & kind)
- {
- reloptions[j].gen = relOpts[i];
- reloptions[j].isset = false;
- j++;
- }
- }
- }
-
- /* Done if no options */
- if (PointerIsValid(DatumGetPointer(options)))
- parseRelOptionsInternal(options, validate, reloptions, numoptions);
-
- *numrelopts = numoptions;
- return reloptions;
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
-
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
-
- if (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
-
- return values;
-}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * 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.
*/
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
+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);
}
- 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;
-
- for (i = 0; i < numoptions; i++)
- {
- relopt_value *optval = &options[i];
-
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
+ 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\"."));
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
+ 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);
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
- }
+ 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);
- return palloc0(size);
+ return stdrd_relopt_spec_set;
}
-/*
- * 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;
+static options_spec_set *heap_relopt_spec_set = NULL;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
+static options_spec_set *toast_relopt_spec_set = NULL;
-/*
- * Option parser for anything that uses StdRdOptions.
- */
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- 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));
+ 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;
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
-
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
-
- pfree(options);
+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 rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1946,158 +565,167 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ if (validate && relopts->validators)
+ {
+ int val_count = 0;
+ int i;
+ option_value *val_array;
- i++;
- }
+ foreach(lc, values)
+ val_count++;
+ val_array = palloc(sizeof(option_value) * val_count);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (elems)
- pfree(elems);
+ foreach(lc, relopts->validators)
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
+ pfree(val_array);
+ }
return opts;
-}
-/*
- * 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
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an options spec set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an options spec set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2107,33 +735,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.c
index 6df7f2eaeb..6852cadcab 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -16,7 +16,7 @@
#include "access/gin_private.h"
#include "access/ginxlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/xloginsert.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
@@ -28,6 +28,7 @@
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/typcache.h"
+#include "utils/guc.h"
/*
@@ -67,7 +68,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = ginvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = ginvalidate;
@@ -82,6 +82,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -604,21 +605,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
*
@@ -705,3 +691,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.c
index 30069f139c..bdb81dc8e9 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -89,7 +89,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;
@@ -104,6 +103,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.c
index 1532462317..624bf965b0 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 */
+static 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.c
index c361509d68..fdd8ae684d 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -86,7 +86,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;
@@ -101,6 +100,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.c
index 32822dbb6b..c745c7316a 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.c
index b52eca8f38..f7b0927f0b 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"
@@ -125,7 +126,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;
@@ -140,6 +140,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1419,3 +1420,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.c
index ff260c393a..a08004c045 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,23 +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.c
index 2c661fcf96..676cc9d4cd 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"
@@ -73,7 +73,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;
@@ -88,6 +87,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -732,22 +732,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).
@@ -1347,3 +1331,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.c
index 152c29b551..6e9d0f2460 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.c
index 91f4dd30de..da4d5da896 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -112,7 +112,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 659e189549..6071e493d3 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"
@@ -547,7 +548,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;
@@ -862,7 +863,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -876,10 +877,18 @@ 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.
@@ -2116,8 +2125,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
palloc0_array(Datum, indexInfo->ii_NumIndexAttrs);
indexInfo->ii_OpclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
attn++;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6007e10730..d5e8630086 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"
@@ -676,7 +677,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;
@@ -824,19 +824,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);
- break;
+ {
+ /* 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)
@@ -4120,7 +4137,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);
@@ -4162,7 +4179,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.
@@ -4383,9 +4400,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8194,12 +8216,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));
@@ -14178,12 +14201,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 */
@@ -14203,38 +14227,56 @@ 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))));
+ 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),
@@ -14244,11 +14286,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;
@@ -14323,20 +14369,23 @@ 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.c
index 45b30ca566..1f76ae9790 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -339,10 +339,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
@@ -1058,11 +1057,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.c
index 56fcb8edf1..fabb70e8bd 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -80,7 +80,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
if (isnull)
fdw->options = NIL;
else
- fdw->options = untransformRelOptions(datum);
+ fdw->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -167,7 +167,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
if (isnull)
server->options = NIL;
else
- server->options = untransformRelOptions(datum);
+ server->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -235,7 +235,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -272,7 +272,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -305,7 +305,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -513,7 +513,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 */
@@ -610,7 +610,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 8140e79d8f..0bbb12b846 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1767,7 +1767,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;
@@ -1831,7 +1831,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 247d0816ad..54a0536307 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1163,6 +1163,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;
@@ -1186,15 +1187,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);
@@ -1303,9 +1305,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 9e252a0891..cc3b910379 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -16,6 +16,7 @@
*/
#include "postgres.h"
+#include "access/options.h"
#include "access/reloptions.h"
#include "utils/attoptcache.h"
#include "utils/catcache.h"
@@ -148,7 +149,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index bd6cd4e47b..71e1ac5765 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -462,7 +462,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -477,11 +477,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;
@@ -492,7 +492,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index 5609246c2f..affe4cc2b4 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/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 4ccb90c197..7643bd34a0 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -755,7 +755,7 @@ AppendStringCommandOption(PQExpBuffer buf, bool use_new_option_syntax,
{
AppendPlainCommandOption(buf, use_new_option_syntax, option_name);
- if (option_value != NULL)
+ if (option_value !=NULL)
{
size_t length = strlen(option_value);
char *escaped_value = palloc(1 + 2 * length);
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 1dc674d230..afb619f8c4 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -136,10 +136,6 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
double *indexCorrelation,
double *indexPages);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
-
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -186,6 +182,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* get catalog of reloptions definitions */
+typedef void *(*amreloptspecset_function) ();
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -263,7 +262,6 @@ typedef struct IndexAmRoutine
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
- amoptions_function amoptions;
amproperty_function amproperty; /* can be NULL */
ambuildphasename_function ambuildphasename; /* can be NULL */
amvalidate_function amvalidate;
@@ -275,6 +273,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amreloptspecset_function amreloptspecset; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 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.h
index 2518660927..a7dbb8a5f8 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -14,6 +14,7 @@
#include "access/amapi.h"
#include "storage/bufpage.h"
#include "utils/typcache.h"
+#include "access/options.h"
/*
@@ -108,6 +109,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern void *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 145ec7743c..4a5a87d002 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -108,6 +108,7 @@ extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
+extern void *gingetreloptspecset(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 093bf23443..5f1a2dbd92 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.h
index da372841c4..12ff5ffcbb 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -472,6 +471,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 8e4f6864e5..e40566b238 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1253,7 +1253,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 0000000000..dc5654d31c
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,262 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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 *namspace; /* 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 *namspace;
+ 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 *namspace,
+ 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_namspaces);
+extern List *optionsDefListFilterNamespaces(List *defList,
+ const char *namspace);
+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.h
index 21bde78ed0..93acc02cf7 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,40 @@
#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 */
+ 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 *relopts, Size relopt_struct_size);
extern void register_reloptions_validator(local_relopts *relopts,
@@ -210,7 +67,7 @@ extern void add_local_real_reloption(local_relopts *relopts, 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 *relopts, const char *name,
@@ -219,29 +76,17 @@ extern void add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, char *validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index 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.h
index 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.h
index 03f14d6be1..493fa0ce5d 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -34,7 +34,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index 67c30394c8..d715c7f2c1 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"
@@ -23,11 +23,7 @@
PG_MODULE_MAGIC;
-/* parse table for fillRelOptions */
-relopt_parse_elt di_relopt_tab[6];
-
-/* Kind of relation options for dummy index */
-relopt_kind di_relopt_kind;
+void _PG_init(void);
typedef enum DummyAmEnum
{
@@ -47,7 +43,7 @@ typedef struct DummyIndexOptions
int option_string_null_offset;
} DummyIndexOptions;
-relopt_enum_elt_def dummyAmEnumValues[] =
+static opt_enum_elt_def dummyAmEnumValues[] =
{
{"one", DUMMY_AM_ENUM_ONE},
{"two", DUMMY_AM_ENUM_TWO},
@@ -61,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.
*/
@@ -216,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.
*/
@@ -306,7 +294,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;
@@ -320,12 +307,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.out
index 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.sql
index 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
--
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 9683b0a88e..ed0db93d81 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3099,9 +3099,9 @@ amgettuple_function
aminitparallelscan_function
aminsert_function
ammarkpos_function
-amoptions_function
amparallelrescan_function
amproperty_function
+amreloptspecset_function
amrescan_function
amrestrpos_function
amvacuumcleanup_function
@@ -3257,6 +3257,7 @@ file_type_t
filehash_hash
filehash_iterator
filemap_t
+fill_string_option
fill_string_relopt
finalize_primnode_context
find_dependent_phvs_context
@@ -3384,7 +3385,6 @@ libpq_source
line_t
lineno_t
list_sort_comparator
-local_relopt
local_relopts
local_source
locale_t
@@ -3442,6 +3442,18 @@ oidvector
on_dsm_detach_callback
on_exit_nicely_callback
openssl_tls_init_hook_typ
+opt_enum_elt_def
+option_spec_basic
+option_spec_bool
+option_spec_enum
+option_spec_int
+option_spec_real
+option_spec_string
+option_type
+option_value
+option_value_postvalidate
+option_value_status
+options_spec_set
ossl_EVP_cipher_func
other
output_type
@@ -3553,6 +3565,7 @@ pointer
polymorphic_actuals
pos_trgm
post_parse_analyze_hook_type
+postprocess_bytea_options_function
postprocess_result_function
pqbool
pqsigfunc
@@ -3608,16 +3621,6 @@ registered_buffer
regmatch_t
regoff_t
regproc
-relopt_bool
-relopt_enum
-relopt_enum_elt_def
-relopt_gen
-relopt_int
-relopt_kind
-relopt_parse_elt
-relopt_real
-relopt_string
-relopt_type
relopt_value
relopts_validator
remoteConn
@@ -3764,6 +3767,7 @@ uuid_sortsupport_state
uuid_t
va_list
vacuumingOptions
+validate_string_option
validate_string_relopt
varatt_expanded
varattrib_1b
--
2.30.2
В письме от воскресенье, 6 ноября 2022 г. 19:22:09 MSK пользователь Nikolay
Shaplov написал:
cfbot reports the patch no longer applies. As CommitFest 2022-11 is
currently underway, this would be an excellent time to update the
patch.Oups! I should have done it before...
FixedTrying to fix meson build
Trying to fix compiler warnings.
Patched rebased. Imported changes from 4f981df8 commit (Report a more useful
error for reloptions on a partitioned table)
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
new_options_take_two_v03f.patchtext/x-patch; charset=UTF-8; name=new_options_take_two_v03f.patchDownload
From d96e50bccdb3ce405be91ac80d6a46723665bd68 Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Sun, 20 Nov 2022 09:07:23 +0300
Subject: [PATCH 03f] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
src/backend/access/brin/brin.c | 47 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/meson.build | 1 +
src/backend/access/common/options.c | 1366 ++++++++++
src/backend/access/common/reloptions.c | 2227 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 19 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/bin/pg_basebackup/streamutil.c | 2 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 2 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 262 ++
src/include/access/reloptions.h | 205 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 148 +-
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
src/tools/pgindent/typedefs.list | 28 +-
47 files changed, 2590 insertions(+), 2321 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 6d58e22813..ce7e5a22d1 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"
@@ -206,7 +207,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern void *blrelopt_specset(void);
+extern void blReloptionPostprocess(void *, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index a434cf93ef..522da7dc9f 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,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* 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 +443,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/dblink/dblink.c b/contrib/dblink/dblink.c
index 04095a8f0e..eeeb83f886 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1976,7 +1976,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 67821cd25b..b3ceb1580a 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -196,7 +196,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index fa80ee2a55..42848b35e1 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -71,7 +71,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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index e89dc74a5e..af46f0a3ee 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 7e386250ae..dd8f6de8d4 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"
@@ -41,7 +40,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);
}
@@ -962,23 +960,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.
@@ -1790,3 +1771,29 @@ 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/Makefile
index 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/meson.build b/src/backend/access/common/meson.build
index 857beaa32d..fe8c1274a4 100644
--- a/src/backend/access/common/meson.build
+++ b/src/backend/access/common/meson.build
@@ -4,6 +4,7 @@ backend_sources += files(
'detoast.c',
'heaptuple.c',
'indextuple.c',
+ 'options.c',
'printsimple.c',
'printtup.c',
'relation.c',
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 0000000000..c626df3505
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1366 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 *namspace, 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 (namspace)
+ {
+ spec_set->namspace = palloc(strlen(namspace) + 1);
+ strcpy(spec_set->namspace, namspace);
+ }
+ else
+ spec_set->namspace = NULL;
+ if (num_items_expected > 0)
+ {
+ spec_set->num_allocated = num_items_expected;
+ spec_set->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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->assert_on_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->namspace = palloc(strlen(def->defnamespace) + 1);
+ strcpy(option_dst->namspace, def->defnamespace);
+ }
+ else
+ option_dst->namspace = 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_builtin(array, TEXTOID, &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 j;
+ 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 (j = 0; j < text_len; j = j + pg_mblen(text_str))
+ {
+ if (text_str[j] == '=')
+ {
+ name_len = j;
+ 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->namspace = 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_namspaces array. Items without namspace are also accepted
+ *
+ * Used while validation of syntax parser output. Error is thrown if unproper
+ * namespace is found.
+ */
+void
+optionsDefListValdateNamespaces(List *defList, char **allowed_namspaces)
+{
+ 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_namspaces[i])
+ {
+ if (strcmp(def->defnamespace,
+ allowed_namspaces[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 "namspace" namespace. If "namspace"
+ * is NULL, only namespaseless options are returned
+ */
+List *
+optionsDefListFilterNamespaces(List *defList, const char *namspace)
+{
+ ListCell *cell;
+ List *result = NIL;
+
+ foreach(cell, defList)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ if ((!namspace && !def->defnamespace) ||
+ (namspace && def->defnamespace &&
+ strcmp(namspace, 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_builtin(array, TEXTOID, &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 unparsed 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 values 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->namspace && (!spec_set->namspace ||
+ strcmp(spec_set->namspace, option->namspace) != 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter namespace \"%s\"",
+ option->namspace)));
+
+ 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->namspace)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s.%s\"",
+ option->namspace, 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 conversion.
+ */
+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;
+ if (defList)
+ data = optionsValuesToBytea(new_values, spec_set);
+ else
+ data = NULL;
+
+ spec_set->postprocess_fun(data, true);
+ if (data) 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/reloptions.c b/src/backend/access/common/reloptions.c
index 75b7344891..c1293637b4 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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -489,17 +106,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -507,225 +115,9 @@ static 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 *relopts, Size relopt_struct_size)
{
- relopts->options = NIL;
relopts->validators = NIL;
- relopts->relopt_struct_size = relopt_struct_size;
+ relopts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, true, 0);
}
/*
@@ -751,112 +142,6 @@ register_reloptions_validator(local_relopts *relopts, relopts_validator validato
relopts->validators = lappend(relopts->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);
}
/*
@@ -921,47 +167,8 @@ add_local_int_reloption(local_relopts *relopts, const char *name,
const char *desc, int default_val, int min_val,
int max_val, int offset)
{
- relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
- name, desc, default_val,
- min_val, max_val, 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * init_real_reloption
- * Allocate and initialize a new real reloption
- */
-static relopt_real *
-init_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption;
-
- newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
- name, desc, lockmode);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- return newoption;
-}
-
-/*
- * add_real_reloption
- * Add a new float reloption
- */
-void
-add_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption = init_real_reloption(kinds, name, desc,
- default_val, min_val,
- max_val, lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
}
/*
@@ -973,140 +180,26 @@ add_real_reloption(bits32 kinds, const char *name, const char *desc,
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;
- }
+ double min_val, double max_val, int offset)
+{
+ optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
- return newoption;
}
/*
- * add_string_reloption
- * Add a new string reloption
+ * add_local_enum_reloption
+ * Add a new local enum 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.
+ * 'offset' is offset of int-typed field.
*/
void
-add_string_reloption(bits32 kinds, const char *name, const char *desc,
- const char *default_val, validate_string_relopt validator,
- LOCKMODE lockmode)
+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)
{
- relopt_string *newoption = init_string_reloption(kinds, name, desc,
- default_val,
- validator, NULL,
- lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ members, default_val, detailmsg);
}
/*
@@ -1122,247 +215,8 @@ add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset)
{
- relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- default_val,
- validator, filler,
- 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * Transform a relation options list (list of DefElem) into the text array
- * format that is kept in pg_class.reloptions, including only those options
- * that are in the passed namespace. The output values do not include the
- * namespace.
- *
- * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
- * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
- * reloptions value (possibly NULL), and we replace or remove entries
- * as needed.
- *
- * If acceptOidsOff is true, then we allow oids = false, but throw error when
- * on. This is solely needed for backwards compatibility.
- *
- * Note that this is not responsible for determining whether the options
- * are valid, but it does check that namespaces for all the options given are
- * listed in validnsps. The NULL namespace is always valid and need not be
- * explicitly listed. Passing a NULL pointer means that only the NULL
- * namespace is valid.
- *
- * Both oldOptions and the result are text arrays (or NULL for "default"),
- * but we declare them as Datums to avoid including array.h in reloptions.h.
- */
-Datum
-transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
- char *validnsps[], bool acceptOidsOff, bool isReset)
-{
- Datum result;
- ArrayBuildState *astate;
- ListCell *cell;
-
- /* no change if empty list */
- if (defList == NIL)
- return oldOptions;
-
- /* We build new array using accumArrayResult */
- astate = NULL;
-
- /* Copy any oldOptions that aren't to be replaced */
- if (PointerIsValid(DatumGetPointer(oldOptions)))
- {
- ArrayType *array = DatumGetArrayTypeP(oldOptions);
- Datum *oldoptions;
- int noldoptions;
- int i;
-
- deconstruct_array_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
-/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
- */
-List *
-untransformRelOptions(Datum options)
-{
- List *result = NIL;
- ArrayType *array;
- Datum *optiondatums;
- int noptions;
- int i;
-
- /* Nothing to do if no options */
- if (!PointerIsValid(DatumGetPointer(options)))
- return result;
-
- array = DatumGetArrayTypeP(options);
-
- deconstruct_array_builtin(array, TEXTOID, &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(p);
- }
- result = lappend(result, makeDefElem(s, val, -1));
- }
-
- return result;
+ optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, offset,
+ NULL, default_val, validator, filler);
}
/*
@@ -1379,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,
@@ -1398,544 +253,326 @@ 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_builtin(array, TEXTOID, &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.
+ * 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.
*
- * options is a reloption text array as constructed by transformRelOptions.
- * kind specifies the family of options to be processed.
+ * Fillfactor can be set because it applies only to subsequent changes made to
+ * data blocks, as documented in heapio.c
*
- * 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.
+ * 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.
*
- * 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).
+ * 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.
*
- * 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.
+ * 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.
*/
-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++;
- }
- }
+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)
+ 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);
-
- *numrelopts = numoptions;
- return reloptions;
-}
-
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
+ 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\"."));
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
+ 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 (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
+ 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);
- return values;
+ return stdrd_relopt_spec_set;
}
-/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
- */
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
-
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
- }
+static options_spec_set *heap_relopt_spec_set = NULL;
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
/*
- * 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).
+ * 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 void *
-allocateReloptStruct(Size base, relopt_value *options, int numoptions)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- Size size = base;
- int i;
-
- for (i = 0; i < numoptions; i++)
+ if (data)
{
- relopt_value *optval = &options[i];
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
-
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
-
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-
- 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)
+static options_spec_set *toast_relopt_spec_set = NULL;
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- int i;
- int offset = basesize;
+ if (toast_relopt_spec_set)
+ return toast_relopt_spec_set;
- for (i = 0; i < numoptions; i++)
- {
- int j;
- bool found = false;
+ toast_relopt_spec_set = get_stdrd_relopt_spec_set(false);
+ toast_relopt_spec_set->postprocess_fun = toast_options_postprocess;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+ return toast_relopt_spec_set;
}
/*
- * Option parser for anything that uses StdRdOptions.
+ * Do not allow to set any option on partitioned table
*/
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+static void
+partitioned_options_postprocess(void *data, bool validate)
{
- 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));
+ if (data && validate)
+ ereport(ERROR,
+ errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot specify storage parameters for a partitioned table"),
+ errhint("Specify storage parameters for its leaf partitions, instead."));
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
+options_spec_set *
+get_partitioned_relopt_spec_set(void)
+{
+ if (partitioned_relopt_spec_set)
+ return partitioned_relopt_spec_set;
+ partitioned_relopt_spec_set = get_stdrd_relopt_spec_set(true);
- pfree(options);
+ /* No options for now, so spec set is empty */
+ partitioned_relopt_spec_set->postprocess_fun =
+ partitioned_options_postprocess;
- return rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1946,157 +583,167 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- i++;
- }
+ if (validate && relopts->validators)
+ {
+ int val_count = 0;
+ int i;
+ option_value *val_array;
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ foreach(lc, values)
+ val_count++;
+ val_array = palloc(sizeof(option_value) * val_count);
+
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (elems)
- pfree(elems);
+ foreach(lc, relopts->validators)
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
+ pfree(val_array);
+ }
return opts;
-}
-/*
- * Option parser for partitioned tables
- */
-bytea *
-partitioned_table_reloptions(Datum reloptions, bool validate)
-{
- if (validate && reloptions)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot specify storage parameters for a partitioned table"),
- errhint("Specify storage parameters for its leaf partitions, instead."));
- return NULL;
}
/*
- * Option parser for views
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an options spec set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an options spec set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2106,33 +753,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.c
index 6df7f2eaeb..6852cadcab 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -16,7 +16,7 @@
#include "access/gin_private.h"
#include "access/ginxlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/xloginsert.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
@@ -28,6 +28,7 @@
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/typcache.h"
+#include "utils/guc.h"
/*
@@ -67,7 +68,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = ginvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = ginvalidate;
@@ -82,6 +82,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -604,21 +605,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
*
@@ -705,3 +691,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.c
index 30069f139c..bdb81dc8e9 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -89,7 +89,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;
@@ -104,6 +103,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.c
index 1532462317..624bf965b0 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 */
+static 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.c
index c361509d68..fdd8ae684d 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -86,7 +86,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;
@@ -101,6 +100,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.c
index 32822dbb6b..c745c7316a 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.c
index b52eca8f38..f7b0927f0b 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"
@@ -125,7 +126,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;
@@ -140,6 +140,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1419,3 +1420,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.c
index ff260c393a..a08004c045 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,23 +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.c
index 2c661fcf96..676cc9d4cd 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"
@@ -73,7 +73,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;
@@ -88,6 +87,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -732,22 +732,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).
@@ -1347,3 +1331,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.c
index 152c29b551..6e9d0f2460 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.c
index 55b0be9e1d..aee92d9fee 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -112,7 +112,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 91cee27743..a887abf423 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"
@@ -549,7 +550,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;
@@ -864,7 +865,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -878,10 +879,18 @@ 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.
@@ -2118,8 +2127,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
palloc0_array(Datum, indexInfo->ii_NumIndexAttrs);
indexInfo->ii_OpclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
attn++;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f006807852..32ecde8021 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"
@@ -676,7 +677,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;
@@ -824,19 +824,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);
- break;
+ {
+ /* 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)
@@ -4120,7 +4137,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);
@@ -4162,7 +4179,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.
@@ -4383,9 +4400,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8194,12 +8216,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));
@@ -14178,12 +14201,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 */
@@ -14203,38 +14227,56 @@ 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))));
+ 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),
@@ -14244,11 +14286,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;
@@ -14323,20 +14369,23 @@ 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.c
index 76e100b560..9838868756 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -339,10 +339,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
@@ -1058,11 +1057,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.c
index 56fcb8edf1..fabb70e8bd 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -80,7 +80,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
if (isnull)
fdw->options = NIL;
else
- fdw->options = untransformRelOptions(datum);
+ fdw->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -167,7 +167,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
if (isnull)
server->options = NIL;
else
- server->options = untransformRelOptions(datum);
+ server->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -235,7 +235,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -272,7 +272,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -305,7 +305,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -513,7 +513,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 */
@@ -610,7 +610,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 487eb2041b..90fce581e7 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1767,7 +1767,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;
@@ -1831,7 +1831,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 247d0816ad..54a0536307 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1163,6 +1163,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;
@@ -1186,15 +1187,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);
@@ -1303,9 +1305,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 9e252a0891..cc3b910379 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -16,6 +16,7 @@
*/
#include "postgres.h"
+#include "access/options.h"
#include "access/reloptions.h"
#include "utils/attoptcache.h"
#include "utils/catcache.h"
@@ -148,7 +149,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index bd6cd4e47b..71e1ac5765 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -462,7 +462,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -477,11 +477,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;
@@ -492,7 +492,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index 5609246c2f..affe4cc2b4 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/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 4ccb90c197..7643bd34a0 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -755,7 +755,7 @@ AppendStringCommandOption(PQExpBuffer buf, bool use_new_option_syntax,
{
AppendPlainCommandOption(buf, use_new_option_syntax, option_name);
- if (option_value != NULL)
+ if (option_value !=NULL)
{
size_t length = strlen(option_value);
char *escaped_value = palloc(1 + 2 * length);
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 1dc674d230..afb619f8c4 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -136,10 +136,6 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
double *indexCorrelation,
double *indexPages);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
-
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -186,6 +182,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* get catalog of reloptions definitions */
+typedef void *(*amreloptspecset_function) ();
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -263,7 +262,6 @@ typedef struct IndexAmRoutine
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
- amoptions_function amoptions;
amproperty_function amproperty; /* can be NULL */
ambuildphasename_function ambuildphasename; /* can be NULL */
amvalidate_function amvalidate;
@@ -275,6 +273,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amreloptspecset_function amreloptspecset; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 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.h
index 2518660927..a7dbb8a5f8 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -14,6 +14,7 @@
#include "access/amapi.h"
#include "storage/bufpage.h"
#include "utils/typcache.h"
+#include "access/options.h"
/*
@@ -108,6 +109,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern void *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 145ec7743c..4a5a87d002 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -108,6 +108,7 @@ extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
+extern void *gingetreloptspecset(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 093bf23443..5f1a2dbd92 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.h
index da372841c4..12ff5ffcbb 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -472,6 +471,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 8e4f6864e5..e40566b238 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1253,7 +1253,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 0000000000..dc5654d31c
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,262 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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 *namspace; /* 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 *namspace;
+ 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 *namspace,
+ 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_namspaces);
+extern List *optionsDefListFilterNamespaces(List *defList,
+ const char *namspace);
+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.h
index 21bde78ed0..93acc02cf7 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,40 @@
#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 */
+ 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 *relopts, Size relopt_struct_size);
extern void register_reloptions_validator(local_relopts *relopts,
@@ -210,7 +67,7 @@ extern void add_local_real_reloption(local_relopts *relopts, 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 *relopts, const char *name,
@@ -219,29 +76,17 @@ extern void add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, char *validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index 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.h
index 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.h
index 03f14d6be1..493fa0ce5d 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -34,7 +34,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index 67c30394c8..d715c7f2c1 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"
@@ -23,11 +23,7 @@
PG_MODULE_MAGIC;
-/* parse table for fillRelOptions */
-relopt_parse_elt di_relopt_tab[6];
-
-/* Kind of relation options for dummy index */
-relopt_kind di_relopt_kind;
+void _PG_init(void);
typedef enum DummyAmEnum
{
@@ -47,7 +43,7 @@ typedef struct DummyIndexOptions
int option_string_null_offset;
} DummyIndexOptions;
-relopt_enum_elt_def dummyAmEnumValues[] =
+static opt_enum_elt_def dummyAmEnumValues[] =
{
{"one", DUMMY_AM_ENUM_ONE},
{"two", DUMMY_AM_ENUM_TWO},
@@ -61,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.
*/
@@ -216,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.
*/
@@ -306,7 +294,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;
@@ -320,12 +307,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.out
index 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.sql
index 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
--
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 245aea1dd1..4ca7f6b0d0 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3100,9 +3100,9 @@ amgettuple_function
aminitparallelscan_function
aminsert_function
ammarkpos_function
-amoptions_function
amparallelrescan_function
amproperty_function
+amreloptspecset_function
amrescan_function
amrestrpos_function
amvacuumcleanup_function
@@ -3258,6 +3258,7 @@ file_type_t
filehash_hash
filehash_iterator
filemap_t
+fill_string_option
fill_string_relopt
finalize_primnode_context
find_dependent_phvs_context
@@ -3385,7 +3386,6 @@ libpq_source
line_t
lineno_t
list_sort_comparator
-local_relopt
local_relopts
local_source
locale_t
@@ -3443,6 +3443,18 @@ oidvector
on_dsm_detach_callback
on_exit_nicely_callback
openssl_tls_init_hook_typ
+opt_enum_elt_def
+option_spec_basic
+option_spec_bool
+option_spec_enum
+option_spec_int
+option_spec_real
+option_spec_string
+option_type
+option_value
+option_value_postvalidate
+option_value_status
+options_spec_set
ossl_EVP_cipher_func
other
output_type
@@ -3554,6 +3566,7 @@ pointer
polymorphic_actuals
pos_trgm
post_parse_analyze_hook_type
+postprocess_bytea_options_function
postprocess_result_function
pqbool
pqsigfunc
@@ -3609,16 +3622,6 @@ registered_buffer
regmatch_t
regoff_t
regproc
-relopt_bool
-relopt_enum
-relopt_enum_elt_def
-relopt_gen
-relopt_int
-relopt_kind
-relopt_parse_elt
-relopt_real
-relopt_string
-relopt_type
relopt_value
relopts_validator
remoteConn
@@ -3765,6 +3768,7 @@ uuid_sortsupport_state
uuid_t
va_list
vacuumingOptions
+validate_string_option
validate_string_relopt
varatt_expanded
varattrib_1b
--
2.30.2
On Sun, 20 Nov 2022 at 11:42, Nikolay Shaplov <dhyan@nataraj.su> wrote:
В письме от воскресенье, 6 ноября 2022 г. 19:22:09 MSK пользователь Nikolay
Shaplov написал:cfbot reports the patch no longer applies. As CommitFest 2022-11 is
currently underway, this would be an excellent time to update the
patch.Oups! I should have done it before...
FixedTrying to fix meson build
Trying to fix compiler warnings.
Patched rebased. Imported changes from 4f981df8 commit (Report a more useful
error for reloptions on a partitioned table)
The patch does not apply on top of HEAD as in [1]http://cfbot.cputube.org/patch_41_3536.log, please post a rebased patch:
=== Applying patches on top of PostgreSQL commit ID
92957ed98c5c565362ce665266132a7f08f6b0c0 ===
=== applying patch ./new_options_take_two_v03f.patch
patching file src/include/access/reloptions.h
Hunk #1 FAILED at 1.
1 out of 4 hunks FAILED -- saving rejects to file
src/include/access/reloptions.h.rej
[1]: http://cfbot.cputube.org/patch_41_3536.log
Regards,
Vignesh
On Tue, 3 Jan 2023 at 18:38, vignesh C <vignesh21@gmail.com> wrote:
On Sun, 20 Nov 2022 at 11:42, Nikolay Shaplov <dhyan@nataraj.su> wrote:
В письме от воскресенье, 6 ноября 2022 г. 19:22:09 MSK пользователь Nikolay
Shaplov написал:cfbot reports the patch no longer applies. As CommitFest 2022-11 is
currently underway, this would be an excellent time to update the
patch.Oups! I should have done it before...
FixedTrying to fix meson build
Trying to fix compiler warnings.
Patched rebased. Imported changes from 4f981df8 commit (Report a more useful
error for reloptions on a partitioned table)The patch does not apply on top of HEAD as in [1], please post a rebased patch:
=== Applying patches on top of PostgreSQL commit ID
92957ed98c5c565362ce665266132a7f08f6b0c0 ===
=== applying patch ./new_options_take_two_v03f.patch
patching file src/include/access/reloptions.h
Hunk #1 FAILED at 1.
1 out of 4 hunks FAILED -- saving rejects to file
src/include/access/reloptions.h.rej
There has been no updates on this thread for some time, so this has
been switched as Returned with Feedback. Feel free to open it in the
next commitfest if you plan to continue on this.
Regards,
Vignesh
On 2023-Jan-31, vignesh C wrote:
On Tue, 3 Jan 2023 at 18:38, vignesh C <vignesh21@gmail.com> wrote:
The patch does not apply on top of HEAD as in [1], please post a rebased patch:
=== Applying patches on top of PostgreSQL commit ID
92957ed98c5c565362ce665266132a7f08f6b0c0 ===
=== applying patch ./new_options_take_two_v03f.patch
patching file src/include/access/reloptions.h
Hunk #1 FAILED at 1.
1 out of 4 hunks FAILED -- saving rejects to file
src/include/access/reloptions.h.rejThere has been no updates on this thread for some time, so this has
been switched as Returned with Feedback. Feel free to open it in the
next commitfest if you plan to continue on this.
Well, no feedback has been given, so I'm not sure this is a great
outcome. In the interest of keeping it alive, I've rebased it. It
turns out that the only conflict is with the 2022 -> 2023 copyright line
update.
I have not reviewed it.
--
Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/
"El que vive para el futuro es un iluso, y el que vive para el pasado,
un imbécil" (Luis Adler, "Los tripulantes de la noche")
Attachments:
v4-0001-New-options-engine.patchtext/x-diff; charset=us-asciiDownload
From 47e60962a3dd5156f1dc3edc6bb060e4769af171 Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Wed, 1 Feb 2023 09:52:40 +0100
Subject: [PATCH v4] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
src/backend/access/brin/brin.c | 47 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/meson.build | 1 +
src/backend/access/common/options.c | 1366 ++++++++++
src/backend/access/common/reloptions.c | 2235 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 19 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/bin/pg_basebackup/streamutil.c | 2 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 2 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 262 ++
src/include/access/reloptions.h | 205 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 138 +-
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
src/tools/pgindent/typedefs.list | 28 +-
47 files changed, 2589 insertions(+), 2320 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index efdf9415d1..77c4fcea9b 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"
@@ -206,7 +207,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern void *blrelopt_specset(void);
+extern void blReloptionPostprocess(void *, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index a6d9f09f31..f50f276344 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,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* 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 +443,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/dblink/dblink.c b/contrib/dblink/dblink.c
index 8dd122042b..d7483ccc8a 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1976,7 +1976,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 8ccc167548..1e355b8f88 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -196,7 +196,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 984e4d168a..5729dae482 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -71,7 +71,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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index e89dc74a5e..af46f0a3ee 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index de1427a1e0..591468a8a7 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"
@@ -41,7 +40,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);
}
@@ -962,23 +960,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.
@@ -1790,3 +1771,29 @@ 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/Makefile
index 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/meson.build b/src/backend/access/common/meson.build
index f5ac17b498..ef7d7619ef 100644
--- a/src/backend/access/common/meson.build
+++ b/src/backend/access/common/meson.build
@@ -6,6 +6,7 @@ backend_sources += files(
'detoast.c',
'heaptuple.c',
'indextuple.c',
+ 'options.c',
'printsimple.c',
'printtup.c',
'relation.c',
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 0000000000..c626df3505
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1366 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 *namspace, 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 (namspace)
+ {
+ spec_set->namspace = palloc(strlen(namspace) + 1);
+ strcpy(spec_set->namspace, namspace);
+ }
+ else
+ spec_set->namspace = NULL;
+ if (num_items_expected > 0)
+ {
+ spec_set->num_allocated = num_items_expected;
+ spec_set->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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->assert_on_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->namspace = palloc(strlen(def->defnamespace) + 1);
+ strcpy(option_dst->namspace, def->defnamespace);
+ }
+ else
+ option_dst->namspace = 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_builtin(array, TEXTOID, &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 j;
+ 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 (j = 0; j < text_len; j = j + pg_mblen(text_str))
+ {
+ if (text_str[j] == '=')
+ {
+ name_len = j;
+ 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->namspace = 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_namspaces array. Items without namspace are also accepted
+ *
+ * Used while validation of syntax parser output. Error is thrown if unproper
+ * namespace is found.
+ */
+void
+optionsDefListValdateNamespaces(List *defList, char **allowed_namspaces)
+{
+ 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_namspaces[i])
+ {
+ if (strcmp(def->defnamespace,
+ allowed_namspaces[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 "namspace" namespace. If "namspace"
+ * is NULL, only namespaseless options are returned
+ */
+List *
+optionsDefListFilterNamespaces(List *defList, const char *namspace)
+{
+ ListCell *cell;
+ List *result = NIL;
+
+ foreach(cell, defList)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ if ((!namspace && !def->defnamespace) ||
+ (namspace && def->defnamespace &&
+ strcmp(namspace, 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_builtin(array, TEXTOID, &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 unparsed 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 values 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->namspace && (!spec_set->namspace ||
+ strcmp(spec_set->namspace, option->namspace) != 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter namespace \"%s\"",
+ option->namspace)));
+
+ 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->namspace)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s.%s\"",
+ option->namspace, 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 conversion.
+ */
+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;
+ if (defList)
+ data = optionsValuesToBytea(new_values, spec_set);
+ else
+ data = NULL;
+
+ spec_set->postprocess_fun(data, true);
+ if (data) 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/reloptions.c b/src/backend/access/common/reloptions.c
index 14c23101ad..7730854e2c 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-2023, 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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -489,17 +106,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -507,225 +115,9 @@ static 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 *relopts, Size relopt_struct_size)
{
- relopts->options = NIL;
relopts->validators = NIL;
- relopts->relopt_struct_size = relopt_struct_size;
+ relopts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, true, 0);
}
/*
@@ -751,112 +142,6 @@ register_reloptions_validator(local_relopts *relopts, relopts_validator validato
relopts->validators = lappend(relopts->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);
}
/*
@@ -921,47 +167,8 @@ add_local_int_reloption(local_relopts *relopts, const char *name,
const char *desc, int default_val, int min_val,
int max_val, int offset)
{
- relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
- name, desc, default_val,
- min_val, max_val, 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * init_real_reloption
- * Allocate and initialize a new real reloption
- */
-static relopt_real *
-init_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption;
-
- newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
- name, desc, lockmode);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- return newoption;
-}
-
-/*
- * add_real_reloption
- * Add a new float reloption
- */
-void
-add_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption = init_real_reloption(kinds, name, desc,
- default_val, min_val,
- max_val, lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
}
/*
@@ -975,57 +182,9 @@ add_local_real_reloption(local_relopts *relopts, const char *name,
const char *desc, double default_val,
double min_val, double max_val, int offset)
{
- relopt_real *newoption = init_real_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- default_val, min_val,
- max_val, 0);
+ optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
- 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);
}
/*
@@ -1036,77 +195,11 @@ add_enum_reloption(bits32 kinds, const char *name, const char *desc,
*/
void
add_local_enum_reloption(local_relopts *relopts, const char *name,
- const char *desc, relopt_enum_elt_def *members,
+ const char *desc, opt_enum_elt_def *members,
int default_val, const char *detailmsg, int offset)
{
- relopt_enum *newoption = init_enum_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- members, default_val,
- detailmsg, 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * init_string_reloption
- * Allocate and initialize a new string reloption
- */
-static relopt_string *
-init_string_reloption(bits32 kinds, const char *name, const char *desc,
- const char *default_val,
- validate_string_relopt validator,
- fill_string_relopt filler,
- LOCKMODE lockmode)
-{
- relopt_string *newoption;
-
- /* make sure the validator/default combination is sane */
- if (validator)
- (validator) (default_val);
-
- newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
- name, desc, lockmode);
- newoption->validate_cb = validator;
- newoption->fill_cb = filler;
- if (default_val)
- {
- if (kinds == RELOPT_KIND_LOCAL)
- newoption->default_val = strdup(default_val);
- else
- newoption->default_val = MemoryContextStrdup(TopMemoryContext, default_val);
- newoption->default_len = strlen(default_val);
- newoption->default_isnull = false;
- }
- else
- {
- newoption->default_val = "";
- newoption->default_len = 0;
- newoption->default_isnull = true;
- }
-
- return newoption;
-}
-
-/*
- * add_string_reloption
- * Add a new string reloption
- *
- * "validator" is an optional function pointer that can be used to test the
- * validity of the values. It must elog(ERROR) when the argument string is
- * not acceptable for the variable. Note that the default value must pass
- * the validation.
- */
-void
-add_string_reloption(bits32 kinds, const char *name, const char *desc,
- const char *default_val, validate_string_relopt validator,
- LOCKMODE lockmode)
-{
- relopt_string *newoption = init_string_reloption(kinds, name, desc,
- default_val,
- validator, NULL,
- lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ members, default_val, detailmsg);
}
/*
@@ -1122,247 +215,8 @@ add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset)
{
- relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- default_val,
- validator, filler,
- 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * Transform a relation options list (list of DefElem) into the text array
- * format that is kept in pg_class.reloptions, including only those options
- * that are in the passed namespace. The output values do not include the
- * namespace.
- *
- * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
- * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
- * reloptions value (possibly NULL), and we replace or remove entries
- * as needed.
- *
- * If acceptOidsOff is true, then we allow oids = false, but throw error when
- * on. This is solely needed for backwards compatibility.
- *
- * Note that this is not responsible for determining whether the options
- * are valid, but it does check that namespaces for all the options given are
- * listed in validnsps. The NULL namespace is always valid and need not be
- * explicitly listed. Passing a NULL pointer means that only the NULL
- * namespace is valid.
- *
- * Both oldOptions and the result are text arrays (or NULL for "default"),
- * but we declare them as Datums to avoid including array.h in reloptions.h.
- */
-Datum
-transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
- char *validnsps[], bool acceptOidsOff, bool isReset)
-{
- Datum result;
- ArrayBuildState *astate;
- ListCell *cell;
-
- /* no change if empty list */
- if (defList == NIL)
- return oldOptions;
-
- /* We build new array using accumArrayResult */
- astate = NULL;
-
- /* Copy any oldOptions that aren't to be replaced */
- if (PointerIsValid(DatumGetPointer(oldOptions)))
- {
- ArrayType *array = DatumGetArrayTypeP(oldOptions);
- Datum *oldoptions;
- int noldoptions;
- int i;
-
- deconstruct_array_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
-/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
- */
-List *
-untransformRelOptions(Datum options)
-{
- List *result = NIL;
- ArrayType *array;
- Datum *optiondatums;
- int noptions;
- int i;
-
- /* Nothing to do if no options */
- if (!PointerIsValid(DatumGetPointer(options)))
- return result;
-
- array = DatumGetArrayTypeP(options);
-
- deconstruct_array_builtin(array, TEXTOID, &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(p);
- }
- result = lappend(result, makeDefElem(s, val, -1));
- }
-
- return result;
+ optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, offset,
+ NULL, default_val, validator, filler);
}
/*
@@ -1379,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,
@@ -1398,544 +253,326 @@ 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_builtin(array, TEXTOID, &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);
-}
-
-/*
- * 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.
- */
-static relopt_value *
-parseRelOptions(Datum options, bool validate, relopt_kind kind,
- int *numrelopts)
-{
- relopt_value *reloptions = NULL;
- int numoptions = 0;
- int i;
- int j;
-
- if (need_initialization)
- initialize_reloptions();
-
- /* Build a list of expected options, based on kind */
-
- for (i = 0; relOpts[i]; i++)
- if (relOpts[i]->kinds & kind)
- numoptions++;
-
- if (numoptions > 0)
- {
- reloptions = palloc(numoptions * sizeof(relopt_value));
-
- for (i = 0, j = 0; relOpts[i]; i++)
- {
- if (relOpts[i]->kinds & kind)
- {
- reloptions[j].gen = relOpts[i];
- reloptions[j].isset = false;
- j++;
- }
- }
- }
-
- /* Done if no options */
- if (PointerIsValid(DatumGetPointer(options)))
- parseRelOptionsInternal(options, validate, reloptions, numoptions);
-
- *numrelopts = numoptions;
- return reloptions;
-}
-
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
-
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
-
- if (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
-
- return values;
-}
-
-/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
- */
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
-
- if (option->isset && validate)
+ if (value->values.bool_val)
ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
-
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
- }
-
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("tables declared WITH OIDS are not supported")));
}
/*
- * 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.
+ * Relation options and Lock levels:
*
- * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
- * equivalent).
+ * 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.
*/
-static void *
-allocateReloptStruct(Size base, relopt_value *options, int numoptions)
+
+
+options_spec_set *
+get_stdrd_relopt_spec_set(bool is_heap)
{
- Size size = base;
- int i;
+ options_spec_set *stdrd_relopt_spec_set = allocateOptionsSpecSet(
+ is_heap ? NULL : "toast", sizeof(StdRdOptions), false, 0);
- for (i = 0; i < numoptions; i++)
+ 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)
{
- relopt_value *optval = &options[i];
+ 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);
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
+ 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);
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
+ 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);
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
+ 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);
}
- return palloc0(size);
+ 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);
+
+ return stdrd_relopt_spec_set;
+}
+
+
+static options_spec_set *heap_relopt_spec_set = NULL;
+
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
/*
- * 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.
+ * 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 void
-fillRelOptions(void *rdopts, Size basesize,
- relopt_value *options, int numoptions,
- bool validate,
- const relopt_parse_elt *elems, int numelems)
+toast_options_postprocess(void *data, bool validate)
{
- int i;
- int offset = basesize;
-
- for (i = 0; i < numoptions; i++)
+ if (data)
{
- int j;
- bool found = false;
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- 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);
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
- SET_VARSIZE(rdopts, offset);
+}
+
+static options_spec_set *toast_relopt_spec_set = NULL;
+options_spec_set *
+get_toast_relopt_spec_set(void)
+{
+ if (toast_relopt_spec_set)
+ return toast_relopt_spec_set;
+
+ toast_relopt_spec_set = get_stdrd_relopt_spec_set(false);
+ toast_relopt_spec_set->postprocess_fun = toast_options_postprocess;
+
+ return toast_relopt_spec_set;
}
/*
- * Option parser for anything that uses StdRdOptions.
+ * Do not allow to set any option on partitioned table
*/
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+static void
+partitioned_options_postprocess(void *data, bool validate)
{
- 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));
+ if (data && validate)
+ ereport(ERROR,
+ errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot specify storage parameters for a partitioned table"),
+ errhint("Specify storage parameters for its leaf partitions, instead."));
}
-/*
- * 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.
- */
-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 *partitioned_relopt_spec_set = NULL;
+
+options_spec_set *
+get_partitioned_relopt_spec_set(void)
{
- int numoptions;
- relopt_value *options;
- void *rdopts;
+ if (partitioned_relopt_spec_set)
+ return partitioned_relopt_spec_set;
+ partitioned_relopt_spec_set = get_stdrd_relopt_spec_set(true);
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
+ /* No options for now, so spec set is empty */
+ partitioned_relopt_spec_set->postprocess_fun =
+ partitioned_options_postprocess;
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
-
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
-
- pfree(options);
-
- return rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1946,157 +583,167 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
- foreach(lc, relopts->options)
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
+
+ if (validate && relopts->validators)
{
- local_relopt *opt = lfirst(lc);
+ int val_count = 0;
+ int i;
+ option_value *val_array;
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ foreach(lc, values)
+ val_count++;
+ val_array = palloc(sizeof(option_value) * val_count);
- i++;
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
+
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
+
+ foreach(lc, relopts->validators)
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
+
+ pfree(val_array);
}
-
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
-
- foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
-
- if (elems)
- pfree(elems);
-
return opts;
+
}
/*
- * Option parser for partitioned tables
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-bytea *
-partitioned_table_reloptions(Datum reloptions, bool validate)
+static options_spec_set *view_relopt_spec_set = NULL;
+
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- if (validate && reloptions)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot specify storage parameters for a partitioned table"),
- errhint("Specify storage parameters for its leaf partitions, instead."));
- return NULL;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
+
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
+
+ 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);
+
+ 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 view_relopt_spec_set;
}
/*
- * Option parser for views
+ * get_attribute_options_spec_set
+ * Returns an options spec set for heap attributes
*/
-bytea *
-view_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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)}
- };
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
- return (bytea *) build_reloptions(reloptions, validate,
- RELOPT_KIND_VIEW,
- sizeof(ViewOptions),
- tab, lengthof(tab));
-}
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
-{
- StdRdOptions *rdopts;
+ 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);
- switch (relkind)
- {
- 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;
- }
+ 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 attribute_options_spec_set;
}
/*
- * 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)
+ * get_tablespace_options_spec_set
+ * Returns an options spec set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- Assert(amoptions != NULL);
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- 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);
- return amoptions(reloptions, validate);
-}
+ optionsSpecSetAddReal(tablespace_options_spec_set, "seq_page_cost",
+ "Sets the planner's estimate of the cost of a sequentially fetched disk page",
+ ShareUpdateExclusiveLock,
+ offsetof(TableSpaceOpts, seq_page_cost),
+ NULL, -1, 0.0, DBL_MAX);
-/*
- * Option parser for attribute reloptions
- */
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
-{
- static const relopt_parse_elt tab[] = {
- {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
- {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
- };
+ 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
+ );
- 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));
+ 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;
}
/*
@@ -2106,33 +753,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.c
index e7cc452a8a..38123ab198 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -16,7 +16,7 @@
#include "access/gin_private.h"
#include "access/ginxlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/xloginsert.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
@@ -28,6 +28,7 @@
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/typcache.h"
+#include "utils/guc.h"
/*
@@ -67,7 +68,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = ginvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = ginvalidate;
@@ -82,6 +82,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -604,21 +605,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
*
@@ -705,3 +691,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.c
index ba394f08f6..4166c138c6 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -89,7 +89,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;
@@ -104,6 +103,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.c
index 56451fede1..9bf7e8a8f0 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 */
+static 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.c
index eb258337d6..7f6a92beb1 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -86,7 +86,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;
@@ -101,6 +100,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.c
index 88089ce02b..908835a7c9 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.c
index 1cc88da032..4453d6e655 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"
@@ -125,7 +126,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;
@@ -140,6 +140,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1419,3 +1420,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.c
index 8003583c0a..8823b56c26 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,23 +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.c
index 3761f2c193..4203e174f8 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"
@@ -73,7 +73,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;
@@ -88,6 +87,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -732,22 +732,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).
@@ -1347,3 +1331,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.c
index d6c6d514f3..e3fcb3f9db 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.c
index 0ecff545a9..3d2cde76fe 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -112,7 +112,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 16ec0b114e..760c3740b5 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"
@@ -550,7 +551,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;
@@ -865,7 +866,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -879,10 +880,18 @@ 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.
@@ -2120,8 +2129,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
palloc0_array(Datum, indexInfo->ii_NumIndexAttrs);
indexInfo->ii_OpclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
attn++;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7c697a285b..90078266cd 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"
@@ -676,7 +677,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;
@@ -824,19 +824,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);
- break;
+ {
+ /* 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)
@@ -4134,7 +4151,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);
@@ -4176,7 +4193,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.
@@ -4396,9 +4413,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8168,12 +8190,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));
@@ -14157,12 +14180,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 */
@@ -14182,38 +14206,56 @@ 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))));
+ 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),
@@ -14223,11 +14265,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;
@@ -14302,20 +14348,23 @@ 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.c
index 3dfbf6a917..38a25d39a4 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -339,10 +339,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
@@ -1058,11 +1057,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.c
index dca02271dc..a2d866468e 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -80,7 +80,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
if (isnull)
fdw->options = NIL;
else
- fdw->options = untransformRelOptions(datum);
+ fdw->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -167,7 +167,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
if (isnull)
server->options = NIL;
else
- server->options = untransformRelOptions(datum);
+ server->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -235,7 +235,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -272,7 +272,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -305,7 +305,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -513,7 +513,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 */
@@ -610,7 +610,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index f9218f48aa..6a0402af4f 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1763,7 +1763,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;
@@ -1827,7 +1827,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index c7d9d96b45..172a15d1c0 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1163,6 +1163,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;
@@ -1186,15 +1187,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);
@@ -1303,9 +1305,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 28a99f0fc4..c6859384e3 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"
@@ -149,7 +150,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index d171cfcf2f..40be2dfe2a 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -462,7 +462,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -477,11 +477,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;
@@ -492,7 +492,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index aabe6ba64b..0bc4fa3752 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,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/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index e7618f4617..867cd35cb3 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -755,7 +755,7 @@ AppendStringCommandOption(PQExpBuffer buf, bool use_new_option_syntax,
{
AppendPlainCommandOption(buf, use_new_option_syntax, option_name);
- if (option_value != NULL)
+ if (option_value !=NULL)
{
size_t length = strlen(option_value);
char *escaped_value = palloc(1 + 2 * length);
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 4f1f67b4d0..412dd6bbe5 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -136,10 +136,6 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
double *indexCorrelation,
double *indexPages);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
-
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -186,6 +182,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* get catalog of reloptions definitions */
+typedef void *(*amreloptspecset_function) ();
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -263,7 +262,6 @@ typedef struct IndexAmRoutine
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
- amoptions_function amoptions;
amproperty_function amproperty; /* can be NULL */
ambuildphasename_function ambuildphasename; /* can be NULL */
amvalidate_function amvalidate;
@@ -275,6 +273,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amreloptspecset_function amreloptspecset; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index ed66f1b3d5..55ce930e0e 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -36,6 +36,8 @@ typedef struct BrinStatsData
#define BRIN_DEFAULT_PAGES_PER_RANGE 128
+#define BRIN_MIN_PAGES_PER_RANGE 1
+#define BRIN_MAX_PAGES_PER_RANGE 131072
#define BrinGetPagesPerRange(relation) \
(AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
relation->rd_rel->relam == BRIN_AM_OID), \
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index 97ddc925b2..e457f66c41 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -14,6 +14,7 @@
#include "access/amapi.h"
#include "storage/bufpage.h"
#include "utils/typcache.h"
+#include "access/options.h"
/*
@@ -108,6 +109,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern void *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 6da64928b6..5918f8976c 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -108,6 +108,7 @@ extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
+extern void *gingetreloptspecset(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 8af33d7b40..4610281e8e 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.h
index 9e035270a1..2ea2152044 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -474,6 +473,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 8f48960f9d..2503b52613 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1260,7 +1260,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 0000000000..dc5654d31c
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,262 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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 *namspace; /* 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 *namspace;
+ 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 *namspace,
+ 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_namspaces);
+extern List *optionsDefListFilterNamespaces(List *defList,
+ const char *namspace);
+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.h
index 1d5bfa62ff..52f285dde4 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-2023, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -22,178 +17,40 @@
#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;
+/*
+ * backward compatibility aliases so local reloption code of custom validator
+ * can work.
+ */
+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)
-/* 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.
+ * relopts_validator functions is left for backward compatibility for using
+ * with local reloptions. Should not be used elsewhere
*/
-typedef struct relopt_enum_elt_def
-{
- const char *string_val;
- int symbol_val;
-} relopt_enum_elt_def;
-
-typedef struct relopt_enum
-{
- relopt_gen gen;
- relopt_enum_elt_def *members;
- 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);
/* 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 */
+ 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 *relopts, Size relopt_struct_size);
extern void register_reloptions_validator(local_relopts *relopts,
@@ -210,7 +67,7 @@ extern void add_local_real_reloption(local_relopts *relopts, 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 *relopts, const char *name,
@@ -219,29 +76,17 @@ extern void add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, char *validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index fe31d32dbe..0501ad600a 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -189,9 +189,6 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
-/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
-
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index c6ef46fc20..2836f6079b 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -529,6 +529,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page,
extern bool spgproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
+extern void *spggetreloptspecset(void);
/* spgdoinsert.c */
extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index e7c2b91a58..a971ab426d 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -34,7 +34,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index dfb1ebb846..fbec560556 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"
@@ -23,11 +23,7 @@
PG_MODULE_MAGIC;
-/* parse table for fillRelOptions */
-relopt_parse_elt di_relopt_tab[6];
-
-/* Kind of relation options for dummy index */
-relopt_kind di_relopt_kind;
+void _PG_init(void);
typedef enum DummyAmEnum
{
@@ -47,7 +43,7 @@ typedef struct DummyIndexOptions
int option_string_null_offset;
} DummyIndexOptions;
-relopt_enum_elt_def dummyAmEnumValues[] =
+static opt_enum_elt_def dummyAmEnumValues[] =
{
{"one", DUMMY_AM_ENUM_ONE},
{"two", DUMMY_AM_ENUM_TWO},
@@ -61,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();
+ if (di_relopt_specset)
+ return di_relopt_specset;
- 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);
+ di_relopt_specset = allocateOptionsSpecSet(NULL,
+ sizeof(DummyIndexOptions), false, 6);
- 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);
+ optionsSpecSetAddInt(
+ di_relopt_specset, "option_int",
+ "Integer option for dummy_index_am",
+ AccessExclusiveLock,
+ offsetof(DummyIndexOptions, option_int), NULL,
+ 10, -10, 100
+ );
- 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);
+ optionsSpecSetAddReal(
+ di_relopt_specset, "option_real",
+ "Real option for dummy_index_am",
+ AccessExclusiveLock,
+ offsetof(DummyIndexOptions, option_real), NULL,
+ 3.1415, -10, 100
+ );
- 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);
+ 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.
*/
@@ -216,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.
*/
@@ -306,7 +294,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;
@@ -320,12 +307,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.out
index 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.sql
index 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
--
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 24510ac29e..35d0f4e090 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3105,9 +3105,9 @@ amgettuple_function
aminitparallelscan_function
aminsert_function
ammarkpos_function
-amoptions_function
amparallelrescan_function
amproperty_function
+amreloptspecset_function
amrescan_function
amrestrpos_function
amvacuumcleanup_function
@@ -3263,6 +3263,7 @@ file_type_t
filehash_hash
filehash_iterator
filemap_t
+fill_string_option
fill_string_relopt
finalize_primnode_context
find_dependent_phvs_context
@@ -3391,7 +3392,6 @@ libpq_source
line_t
lineno_t
list_sort_comparator
-local_relopt
local_relopts
local_source
locale_t
@@ -3449,6 +3449,18 @@ oidvector
on_dsm_detach_callback
on_exit_nicely_callback
openssl_tls_init_hook_typ
+opt_enum_elt_def
+option_spec_basic
+option_spec_bool
+option_spec_enum
+option_spec_int
+option_spec_real
+option_spec_string
+option_type
+option_value
+option_value_postvalidate
+option_value_status
+options_spec_set
ossl_EVP_cipher_func
other
output_type
@@ -3560,6 +3572,7 @@ pointer
polymorphic_actuals
pos_trgm
post_parse_analyze_hook_type
+postprocess_bytea_options_function
postprocess_result_function
pqbool
pqsigfunc
@@ -3615,16 +3628,6 @@ registered_buffer
regmatch_t
regoff_t
regproc
-relopt_bool
-relopt_enum
-relopt_enum_elt_def
-relopt_gen
-relopt_int
-relopt_kind
-relopt_parse_elt
-relopt_real
-relopt_string
-relopt_type
relopt_value
relopts_validator
remoteConn
@@ -3772,6 +3775,7 @@ uuid_sortsupport_state
uuid_t
va_list
vacuumingOptions
+validate_string_option
validate_string_relopt
varatt_expanded
varattrib_1b
--
2.30.2
В письме от среда, 1 февраля 2023 г. 12:04:26 MSK пользователь Alvaro Herrera
написал:
On 2023-Jan-31, vignesh C wrote:
On Tue, 3 Jan 2023 at 18:38, vignesh C <vignesh21@gmail.com> wrote:
The patch does not apply on top of HEAD as in [1], please post a rebased
patch: === Applying patches on top of PostgreSQL commit ID
92957ed98c5c565362ce665266132a7f08f6b0c0 ===
=== applying patch ./new_options_take_two_v03f.patch
patching file src/include/access/reloptions.h
Hunk #1 FAILED at 1.
1 out of 4 hunks FAILED -- saving rejects to file
src/include/access/reloptions.h.rejThere has been no updates on this thread for some time, so this has
been switched as Returned with Feedback. Feel free to open it in the
next commitfest if you plan to continue on this.Well, no feedback has been given, so I'm not sure this is a great
outcome. In the interest of keeping it alive, I've rebased it. It
turns out that the only conflict is with the 2022 -> 2023 copyright line
update.I have not reviewed it.
Guys sorry, I am totally unable to do just anything this month... Do not have
spare resources to switch my attention to anything even this simple...
Hope, next month will be better... And I will try to do some more reviewing of
other patches...
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
On Wed, 1 Feb 2023 at 14:49, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
On 2023-Jan-31, vignesh C wrote:
On Tue, 3 Jan 2023 at 18:38, vignesh C <vignesh21@gmail.com> wrote:
The patch does not apply on top of HEAD as in [1], please post a rebased patch:
=== Applying patches on top of PostgreSQL commit ID
92957ed98c5c565362ce665266132a7f08f6b0c0 ===
=== applying patch ./new_options_take_two_v03f.patch
patching file src/include/access/reloptions.h
Hunk #1 FAILED at 1.
1 out of 4 hunks FAILED -- saving rejects to file
src/include/access/reloptions.h.rejThere has been no updates on this thread for some time, so this has
been switched as Returned with Feedback. Feel free to open it in the
next commitfest if you plan to continue on this.Well, no feedback has been given, so I'm not sure this is a great
outcome. In the interest of keeping it alive, I've rebased it. It
turns out that the only conflict is with the 2022 -> 2023 copyright line
update.I have not reviewed it.
Since there was no response to rebase the patch, I was not sure if the
author was planning to continue. Anyways Nikolay Shaplov plans to work
on this soon, So I have added this to the next commitfest at [1]https://commitfest.postgresql.org/41/3536/.
[1]: https://commitfest.postgresql.org/41/3536/
Regards,
Vignesh
On 2023-Feb-02, vignesh C wrote:
On Wed, 1 Feb 2023 at 14:49, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
Well, no feedback has been given, so I'm not sure this is a great
outcome. In the interest of keeping it alive, I've rebased it. It
turns out that the only conflict is with the 2022 -> 2023 copyright line
update.Since there was no response to rebase the patch, I was not sure if the
author was planning to continue. Anyways Nikolay Shaplov plans to work
on this soon, So I have added this to the next commitfest at [1].
Thank you!
--
Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/
"¿Cómo puedes confiar en algo que pagas y que no ves,
y no confiar en algo que te dan y te lo muestran?" (Germán Poo)
On Thu, Feb 2, 2023 at 10:03 AM vignesh C <vignesh21@gmail.com> wrote:
On Wed, 1 Feb 2023 at 14:49, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
On 2023-Jan-31, vignesh C wrote:
On Tue, 3 Jan 2023 at 18:38, vignesh C <vignesh21@gmail.com> wrote:
There has been no updates on this thread for some time, so this has
been switched as Returned with Feedback. Feel free to open it in the
next commitfest if you plan to continue on this.Well, no feedback has been given, so I'm not sure this is a great
outcome. In the interest of keeping it alive, I've rebased it. It
turns out that the only conflict is with the 2022 -> 2023 copyright line
update.I have not reviewed it.
Since there was no response to rebase the patch, I was not sure if the
author was planning to continue. Anyways Nikolay Shaplov plans to work
on this soon, So I have added this to the next commitfest at [1].
[1] - https://commitfest.postgresql.org/41/3536/
Nine months have passed, and there hasn't been an update since last
time it was returned. It doesn't seem to have been productive to
resurrect it to the CF based on "someone plans to work on it soon".
I'm re-returning it with feedback.
Some time ago, there was a proposal to create a new category "lack of
interest", but that also seems to have gone nowhere because of...lack
of interest.
--
John Naylor
11.11.2023 12:33, John Naylor пишет:
On Thu, Feb 2, 2023 at 10:03 AM vignesh C <vignesh21@gmail.com> wrote:
On Wed, 1 Feb 2023 at 14:49, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
On 2023-Jan-31, vignesh C wrote:
On Tue, 3 Jan 2023 at 18:38, vignesh C <vignesh21@gmail.com> wrote:
There has been no updates on this thread for some time, so this has
been switched as Returned with Feedback. Feel free to open it in the
next commitfest if you plan to continue on this.Well, no feedback has been given, so I'm not sure this is a great
outcome. In the interest of keeping it alive, I've rebased it. It
turns out that the only conflict is with the 2022 -> 2023 copyright line
update.I have not reviewed it.
Since there was no response to rebase the patch, I was not sure if the
author was planning to continue. Anyways Nikolay Shaplov plans to work
on this soon, So I have added this to the next commitfest at [1].
[1] - https://commitfest.postgresql.org/41/3536/Nine months have passed, and there hasn't been an update since last
time it was returned. It doesn't seem to have been productive to
resurrect it to the CF based on "someone plans to work on it soon".
I'm re-returning it with feedback.Some time ago, there was a proposal to create a new category "lack of
interest", but that also seems to have gone nowhere because of...lack
of interest.
I've rebased patch, so it could be add to commitfest again.
------
Yura Sokolov
Attachments:
v5-0001-New-options-engine.patchtext/x-patch; charset=UTF-8; name=v5-0001-New-options-engine.patchDownload
From 9cfda31e04d1ee615da817df8af622f3b1e8e4f8 Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Wed, 1 Feb 2023 09:52:40 +0100
Subject: [PATCH] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
src/backend/access/brin/brin.c | 47 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/meson.build | 1 +
src/backend/access/common/options.c | 1366 ++++++++++
src/backend/access/common/reloptions.c | 2234 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 19 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/bin/pg_basebackup/streamutil.c | 2 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 2 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 262 ++
src/include/access/reloptions.h | 205 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 148 +-
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
src/tools/pgindent/typedefs.list | 28 +-
47 files changed, 2592 insertions(+), 2326 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 330811ec608..f13535af9ff 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"
@@ -206,7 +207,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern void *blrelopt_specset(void);
+extern void blReloptionPostprocess(void *, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 4830cb3fee6..7e2c281b8d1 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.
*/
@@ -136,7 +96,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;
@@ -155,6 +115,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* Convert signature length from # of bits to # to words, rounding up */
+ opts->bloomLength = (opts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
+}
+
+
/*
* Fill BloomState structure for particular index.
*/
@@ -469,24 +438,37 @@ BloomInitMetapage(Relation index, ForkNumber forknum)
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/dblink/dblink.c b/contrib/dblink/dblink.c
index 27bd0d31fdf..df7d1299098 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1935,7 +1935,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2189be8a3c0..f62b4c8d11b 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -197,7 +197,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 8c822f4ef90..07f3b82b8f5 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -71,7 +71,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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index 517f20bc37e..71581fba349 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 4f2dfdd17b9..463c87e10ec 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"
@@ -41,7 +40,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.
@@ -134,7 +132,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;
@@ -149,6 +146,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = bringetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1032,23 +1030,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.
@@ -1982,3 +1963,29 @@ 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/Makefile
index b9aff0ccfdc..78c9c5a8d25 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/meson.build b/src/backend/access/common/meson.build
index f5ac17b4987..ef7d7619ef6 100644
--- a/src/backend/access/common/meson.build
+++ b/src/backend/access/common/meson.build
@@ -6,6 +6,7 @@ backend_sources += files(
'detoast.c',
'heaptuple.c',
'indextuple.c',
+ 'options.c',
'printsimple.c',
'printtup.c',
'relation.c',
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 00000000000..c626df3505c
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1366 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 *namspace, 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 (namspace)
+ {
+ spec_set->namspace = palloc(strlen(namspace) + 1);
+ strcpy(spec_set->namspace, namspace);
+ }
+ else
+ spec_set->namspace = NULL;
+ if (num_items_expected > 0)
+ {
+ spec_set->num_allocated = num_items_expected;
+ spec_set->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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->assert_on_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->namspace = palloc(strlen(def->defnamespace) + 1);
+ strcpy(option_dst->namspace, def->defnamespace);
+ }
+ else
+ option_dst->namspace = 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_builtin(array, TEXTOID, &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 j;
+ 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 (j = 0; j < text_len; j = j + pg_mblen(text_str))
+ {
+ if (text_str[j] == '=')
+ {
+ name_len = j;
+ 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->namspace = 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_namspaces array. Items without namspace are also accepted
+ *
+ * Used while validation of syntax parser output. Error is thrown if unproper
+ * namespace is found.
+ */
+void
+optionsDefListValdateNamespaces(List *defList, char **allowed_namspaces)
+{
+ 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_namspaces[i])
+ {
+ if (strcmp(def->defnamespace,
+ allowed_namspaces[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 "namspace" namespace. If "namspace"
+ * is NULL, only namespaseless options are returned
+ */
+List *
+optionsDefListFilterNamespaces(List *defList, const char *namspace)
+{
+ ListCell *cell;
+ List *result = NIL;
+
+ foreach(cell, defList)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ if ((!namspace && !def->defnamespace) ||
+ (namspace && def->defnamespace &&
+ strcmp(namspace, 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_builtin(array, TEXTOID, &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 unparsed 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 values 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->namspace && (!spec_set->namspace ||
+ strcmp(spec_set->namspace, option->namspace) != 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter namespace \"%s\"",
+ option->namspace)));
+
+ 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->namspace)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s.%s\"",
+ option->namspace, 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 conversion.
+ */
+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;
+ if (defList)
+ data = optionsValuesToBytea(new_values, spec_set);
+ else
+ data = NULL;
+
+ spec_set->postprocess_fun(data, true);
+ if (data) 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/reloptions.c b/src/backend/access/common/reloptions.c
index c32bb7d2b64..6d751f31c86 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-2023, 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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -489,17 +106,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -507,225 +115,9 @@ static 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 *relopts, Size relopt_struct_size)
{
- relopts->options = NIL;
relopts->validators = NIL;
- relopts->relopt_struct_size = relopt_struct_size;
+ relopts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, true, 0);
}
/*
@@ -751,112 +142,6 @@ register_reloptions_validator(local_relopts *relopts, relopts_validator validato
relopts->validators = lappend(relopts->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);
}
/*
@@ -921,47 +167,8 @@ add_local_int_reloption(local_relopts *relopts, const char *name,
const char *desc, int default_val, int min_val,
int max_val, int offset)
{
- relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
- name, desc, default_val,
- min_val, max_val, 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * init_real_reloption
- * Allocate and initialize a new real reloption
- */
-static relopt_real *
-init_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption;
-
- newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
- name, desc, lockmode);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- return newoption;
-}
-
-/*
- * add_real_reloption
- * Add a new float reloption
- */
-void
-add_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption = init_real_reloption(kinds, name, desc,
- default_val, min_val,
- max_val, lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
}
/*
@@ -971,142 +178,28 @@ add_real_reloption(bits32 kinds, const char *name, const char *desc,
* '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;
- }
+add_local_real_reloption(local_relopts *relopts, const char *name,
+ const char *desc, double default_val,
+ double min_val, double max_val, int offset)
+{
+ optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
- return newoption;
}
/*
- * add_string_reloption
- * Add a new string reloption
+ * add_local_enum_reloption
+ * Add a new local enum 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.
+ * 'offset' is offset of int-typed field.
*/
void
-add_string_reloption(bits32 kinds, const char *name, const char *desc,
- const char *default_val, validate_string_relopt validator,
- LOCKMODE lockmode)
+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)
{
- relopt_string *newoption = init_string_reloption(kinds, name, desc,
- default_val,
- validator, NULL,
- lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ members, default_val, detailmsg);
}
/*
@@ -1122,247 +215,8 @@ add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset)
{
- relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- default_val,
- validator, filler,
- 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * Transform a relation options list (list of DefElem) into the text array
- * format that is kept in pg_class.reloptions, including only those options
- * that are in the passed namespace. The output values do not include the
- * namespace.
- *
- * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
- * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
- * reloptions value (possibly NULL), and we replace or remove entries
- * as needed.
- *
- * If acceptOidsOff is true, then we allow oids = false, but throw error when
- * on. This is solely needed for backwards compatibility.
- *
- * Note that this is not responsible for determining whether the options
- * are valid, but it does check that namespaces for all the options given are
- * listed in validnsps. The NULL namespace is always valid and need not be
- * explicitly listed. Passing a NULL pointer means that only the NULL
- * namespace is valid.
- *
- * Both oldOptions and the result are text arrays (or NULL for "default"),
- * but we declare them as Datums to avoid including array.h in reloptions.h.
- */
-Datum
-transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
- char *validnsps[], bool acceptOidsOff, bool isReset)
-{
- Datum result;
- ArrayBuildState *astate;
- ListCell *cell;
-
- /* no change if empty list */
- if (defList == NIL)
- return oldOptions;
-
- /* We build new array using accumArrayResult */
- astate = NULL;
-
- /* Copy any oldOptions that aren't to be replaced */
- if (PointerIsValid(DatumGetPointer(oldOptions)))
- {
- ArrayType *array = DatumGetArrayTypeP(oldOptions);
- Datum *oldoptions;
- int noldoptions;
- int i;
-
- deconstruct_array_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
-/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
- */
-List *
-untransformRelOptions(Datum options)
-{
- List *result = NIL;
- ArrayType *array;
- Datum *optiondatums;
- int noptions;
- int i;
-
- /* Nothing to do if no options */
- if (!PointerIsValid(DatumGetPointer(options)))
- return result;
-
- array = DatumGetArrayTypeP(options);
-
- deconstruct_array_builtin(array, TEXTOID, &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(p);
- }
- result = lappend(result, makeDefElem(s, val, -1));
- }
-
- return result;
+ optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, offset,
+ NULL, default_val, validator, filler);
}
/*
@@ -1379,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,
@@ -1398,544 +253,326 @@ 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_builtin(array, TEXTOID, &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.
+ * 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.
*
- * options is a reloption text array as constructed by transformRelOptions.
- * kind specifies the family of options to be processed.
+ * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
+ * since they are only used by the AV procs and don't change anything
+ * currently executing.
*
- * 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.
+ * Fillfactor can be set because it applies only to subsequent changes made to
+ * data blocks, as documented in heapio.c
*
- * 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).
+ * 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.
*
- * 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.
- */
-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++;
- }
- }
+ * 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.
+ */
+
+
+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)
+ 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);
-
- *numrelopts = numoptions;
- return reloptions;
-}
-
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
+ 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\"."));
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
+ 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 (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
+ 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);
- return values;
+ return stdrd_relopt_spec_set;
}
-/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
- */
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
-
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
- }
+static options_spec_set *heap_relopt_spec_set = NULL;
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
/*
- * 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).
+ * 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 void *
-allocateReloptStruct(Size base, relopt_value *options, int numoptions)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- Size size = base;
- int i;
-
- for (i = 0; i < numoptions; i++)
+ if (data)
{
- relopt_value *optval = &options[i];
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
-
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
-
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-
- 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)
+static options_spec_set *toast_relopt_spec_set = NULL;
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- int i;
- int offset = basesize;
+ if (toast_relopt_spec_set)
+ return toast_relopt_spec_set;
- for (i = 0; i < numoptions; i++)
- {
- int j;
- bool found = false;
+ toast_relopt_spec_set = get_stdrd_relopt_spec_set(false);
+ toast_relopt_spec_set->postprocess_fun = toast_options_postprocess;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+ return toast_relopt_spec_set;
}
/*
- * Option parser for anything that uses StdRdOptions.
+ * Do not allow to set any option on partitioned table
*/
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+static void
+partitioned_options_postprocess(void *data, bool validate)
{
- 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));
+ if (data && validate)
+ ereport(ERROR,
+ errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot specify storage parameters for a partitioned table"),
+ errhint("Specify storage parameters for its leaf partitions instead."));
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
+options_spec_set *
+get_partitioned_relopt_spec_set(void)
+{
+ if (partitioned_relopt_spec_set)
+ return partitioned_relopt_spec_set;
+ partitioned_relopt_spec_set = get_stdrd_relopt_spec_set(true);
- pfree(options);
+ /* No options for now, so spec set is empty */
+ partitioned_relopt_spec_set->postprocess_fun =
+ partitioned_options_postprocess;
- return rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1946,158 +583,165 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
+
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- foreach(lc, relopts->options)
+ if (validate && relopts->validators)
{
- local_relopt *opt = lfirst(lc);
+ int val_count = list_length(values);
+ int i;
+ option_value *val_array;
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ val_array = palloc(sizeof(option_value) * val_count);
- i++;
- }
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (validate)
foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
- if (elems)
- pfree(elems);
+ pfree(val_array);
+ }
return opts;
}
/*
- * Option parser for partitioned tables
- */
-bytea *
-partitioned_table_reloptions(Datum reloptions, bool validate)
-{
- if (validate && reloptions)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot specify storage parameters for a partitioned table"),
- errhint("Specify storage parameters for its leaf partitions instead."));
- return NULL;
-}
-
-/*
- * Option parser for views
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an options spec set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an options spec set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2107,33 +751,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.c
index a875c5d3d7a..fd57aae0baa 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"
/*
@@ -69,7 +70,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;
@@ -84,6 +84,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -598,21 +599,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
*
@@ -699,3 +685,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.c
index 9a1bf8f66cb..0d4dbce7c44 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -91,7 +91,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;
@@ -106,6 +105,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.c
index 9ce3687dbf1..92b5e7d6cf0 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"
@@ -908,20 +908,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.
*
@@ -1056,3 +1042,39 @@ gistGetFakeLSN(Relation rel)
return GetFakeLSNForUnloggedRel();
}
}
+
+/* values from GistOptBufferingMode */
+static 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.c
index 6443ff21bda..ff1b57b3cc5 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -88,7 +88,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;
@@ -103,6 +102,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.c
index 88089ce02ba..908835a7c90 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.c
index 0930f9b37e3..d499ebebbdf 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"
@@ -127,7 +128,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;
@@ -142,6 +142,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1428,3 +1429,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.c
index 1510b97fbe1..307d39c2ed2 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"
@@ -2155,23 +2155,6 @@ BTreeShmemInit(void)
Assert(found);
}
-bytea *
-btoptions(Datum reloptions, bool validate)
-{
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(BTOptions, fillfactor)},
- {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
- offsetof(BTOptions, vacuum_cleanup_index_scale_factor)},
- {"deduplicate_items", RELOPT_TYPE_BOOL,
- offsetof(BTOptions, deduplicate_items)}
- };
-
- return (bytea *) build_reloptions(reloptions, validate,
- RELOPT_KIND_BTREE,
- sizeof(BTOptions),
- tab, lengthof(tab));
-}
-
/*
* btproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 30c00876a56..6a220bc245e 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"
@@ -75,7 +75,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;
@@ -90,6 +89,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -725,22 +725,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).
@@ -1340,3 +1324,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.c
index e91920ca14a..8007d5eae4a 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.c
index 0ecff545a9e..3d2cde76fe1 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -112,7 +112,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index e56205abd8c..2ed114313d2 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"
@@ -558,7 +559,7 @@ DefineIndex(Oid tableId,
IndexAmRoutine *amRoutine;
bool amcanorder;
bool amissummarizing;
- amoptions_function amoptions;
+ amreloptspecset_function amreloptspecsetfn;
bool partitioned;
bool safe_index;
Datum reloptions;
@@ -867,7 +868,7 @@ DefineIndex(Oid tableId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
amissummarizing = amRoutine->amsummarizing;
pfree(amRoutine);
@@ -882,10 +883,18 @@ DefineIndex(Oid tableId,
/*
* 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.
@@ -2191,8 +2200,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
Assert(attn < nkeycols);
opclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
else
opclassOptions[attn] = (Datum) 0;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6b0a20010e2..b641c648ee8 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"
@@ -690,7 +691,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;
@@ -838,19 +838,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);
- break;
+ {
+ /* 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)
@@ -4395,7 +4412,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);
@@ -4437,7 +4454,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.
@@ -4658,9 +4675,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8596,12 +8618,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));
@@ -14937,12 +14960,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 */
@@ -14962,38 +14986,56 @@ 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))));
+ 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),
@@ -15003,11 +15045,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;
@@ -15082,20 +15128,23 @@ 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.c
index 13b0dee1468..b927c445f41 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -339,10 +339,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
@@ -1058,11 +1057,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.c
index fc3edef2a86..b3e607ca54a 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -80,7 +80,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
if (isnull)
fdw->options = NIL;
else
- fdw->options = untransformRelOptions(datum);
+ fdw->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -167,7 +167,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
if (isnull)
server->options = NIL;
else
- server->options = untransformRelOptions(datum);
+ server->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -239,7 +239,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -276,7 +276,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -309,7 +309,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -517,7 +517,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 */
@@ -615,7 +615,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index cf0d432ab13..57541feae74 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1884,7 +1884,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;
@@ -1948,7 +1948,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 366a27ae8ed..7a3a2fd54e8 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1163,6 +1163,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;
@@ -1186,15 +1187,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);
@@ -1303,9 +1305,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 6769d4765b1..a43e5514e7c 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"
@@ -149,7 +150,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index b3faccbefe5..0872addabcf 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -464,7 +464,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -479,11 +479,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;
@@ -494,7 +494,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index 136fd737d37..ffc5bbee5b6 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,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/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index dbd08ab1722..59dfa8cc054 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -763,7 +763,7 @@ AppendStringCommandOption(PQExpBuffer buf, bool use_new_option_syntax,
{
AppendPlainCommandOption(buf, use_new_option_syntax, option_name);
- if (option_value != NULL)
+ if (option_value !=NULL)
{
size_t length = strlen(option_value);
char *escaped_value = palloc(1 + 2 * length);
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 244459587fc..66a4e5d6e80 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -139,10 +139,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,
@@ -189,6 +185,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.
*/
@@ -269,7 +268,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;
@@ -281,6 +279,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amreloptspecset_function amreloptspecset; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index ed66f1b3d51..55ce930e0ed 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -36,6 +36,8 @@ typedef struct BrinStatsData
#define BRIN_DEFAULT_PAGES_PER_RANGE 128
+#define BRIN_MIN_PAGES_PER_RANGE 1
+#define BRIN_MAX_PAGES_PER_RANGE 131072
#define BrinGetPagesPerRange(relation) \
(AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
relation->rd_rel->relam == BRIN_AM_OID), \
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index ed231e208eb..7708930abc1 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"
/*
@@ -109,6 +110,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern void *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index d1df9827f39..c8b6d06725b 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -108,6 +108,7 @@ extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
+extern void *gingetreloptspecset(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 82eb7b4bd8f..07752530589 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
*/
@@ -479,7 +481,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
#define GIST_MIN_FILLFACTOR 10
#define GIST_DEFAULT_FILLFACTOR 90
-extern bytea *gistoptions(Datum reloptions, bool validate);
+extern void *gistgetreloptspecset(void);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 4806ce6c4ea..2baf5d597bb 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -474,6 +473,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 7bfbf3086c8..fc40e505153 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1267,7 +1267,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 00000000000..dc5654d31c9
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,262 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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 *namspace; /* 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 *namspace;
+ 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 *namspace,
+ 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_namspaces);
+extern List *optionsDefListFilterNamespaces(List *defList,
+ const char *namspace);
+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.h
index 3602397cf51..52f285dde49 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-2023, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -22,178 +17,40 @@
#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 */
+ 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 *relopts, Size relopt_struct_size);
extern void register_reloptions_validator(local_relopts *relopts,
@@ -210,7 +67,7 @@ extern void add_local_real_reloption(local_relopts *relopts, 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 *relopts, const char *name,
@@ -219,29 +76,17 @@ extern void add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, char *validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index 480e38ad962..3d3603c0b93 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -189,9 +189,6 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
-/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
-
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index bc39ee45ccf..ab6d99fc516 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -529,6 +529,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page,
extern bool spgproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
+extern void *spggetreloptspecset(void);
/* spgdoinsert.c */
extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index a9c68256017..940346c7fae 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -36,7 +36,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index cbdae7ab7a5..b5424575935 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"
@@ -23,11 +23,7 @@
PG_MODULE_MAGIC;
-/* parse table for fillRelOptions */
-relopt_parse_elt di_relopt_tab[6];
-
-/* Kind of relation options for dummy index */
-relopt_kind di_relopt_kind;
+void _PG_init(void);
typedef enum DummyAmEnum
{
@@ -47,7 +43,7 @@ typedef struct DummyIndexOptions
int option_string_null_offset;
} DummyIndexOptions;
-relopt_enum_elt_def dummyAmEnumValues[] =
+static opt_enum_elt_def dummyAmEnumValues[] =
{
{"one", DUMMY_AM_ENUM_ONE},
{"two", DUMMY_AM_ENUM_TWO},
@@ -61,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.
*/
@@ -216,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.
*/
@@ -307,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;
@@ -321,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.out
index b6aef6f654c..72fee4176b7 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.sql
index 4252b0202f4..fadce3384d4 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
--
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 38a86575e1c..5fe29986911 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3168,9 +3168,9 @@ amgettuple_function
aminitparallelscan_function
aminsert_function
ammarkpos_function
-amoptions_function
amparallelrescan_function
amproperty_function
+amreloptspecset_function
amrescan_function
amrestrpos_function
amvacuumcleanup_function
@@ -3331,6 +3331,7 @@ file_type_t
filehash_hash
filehash_iterator
filemap_t
+fill_string_option
fill_string_relopt
finalize_primnode_context
find_dependent_phvs_context
@@ -3460,7 +3461,6 @@ libpq_source
line_t
lineno_t
list_sort_comparator
-local_relopt
local_relopts
local_source
locale_t
@@ -3519,6 +3519,18 @@ oidvector
on_dsm_detach_callback
on_exit_nicely_callback
openssl_tls_init_hook_typ
+opt_enum_elt_def
+option_spec_basic
+option_spec_bool
+option_spec_enum
+option_spec_int
+option_spec_real
+option_spec_string
+option_type
+option_value
+option_value_postvalidate
+option_value_status
+options_spec_set
ossl_EVP_cipher_func
other
output_type
@@ -3633,6 +3645,7 @@ pointer
polymorphic_actuals
pos_trgm
post_parse_analyze_hook_type
+postprocess_bytea_options_function
postprocess_result_function
pqbool
pqsigfunc
@@ -3692,16 +3705,6 @@ registered_buffer
regmatch_t
regoff_t
regproc
-relopt_bool
-relopt_enum
-relopt_enum_elt_def
-relopt_gen
-relopt_int
-relopt_kind
-relopt_parse_elt
-relopt_real
-relopt_string
-relopt_type
relopt_value
relopts_validator
remoteConn
@@ -3858,6 +3861,7 @@ uuid_sortsupport_state
uuid_t
va_list
vacuumingOptions
+validate_string_option
validate_string_relopt
varatt_expanded
varattrib_1b
--
2.40.1
On Fri, Dec 08, 2023 at 08:10:29AM +0300, Yura Sokolov wrote:
I've rebased patch, so it could be add to commitfest again.
This is a 270kB patch with quite a few changes, and a lot of code
moved around:
47 files changed, 2592 insertions(+), 2326 deletions(-)
Could it be possible to split that into more successive steps to ease
its review?
--
Michael
В письме от пятница, 8 декабря 2023 г. 08:59:41 MSK пользователь Michael
Paquier написал:
I've rebased patch, so it could be add to commitfest again.
This is a 270kB patch with quite a few changes, and a lot of code
moved around:
47 files changed, 2592 insertions(+), 2326 deletions(-)
Could it be possible to split that into more successive steps to ease
its review?
Theoretically I can create patch with full options.c as it is in the patch
now, and use that code only in index AM, and keep reloption.c mostly
unchanged.
This will be total mess with two different options mechanisms working in the
same time, but this might be much more easy to review. When we are done with
the first step, we can change the rest.
If this will help to finally include patch into postgres, I can do it. Will
that help you to review?
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
On 2023-Dec-08, Nikolay Shaplov wrote:
Theoretically I can create patch with full options.c as it is in the patch
now, and use that code only in index AM, and keep reloption.c mostly
unchanged.This will be total mess with two different options mechanisms working in the
same time, but this might be much more easy to review. When we are done with
the first step, we can change the rest.
If this will help to finally include patch into postgres, I can do it. Will
that help you to review?
I don't think that's better, because we could create slight
inconsistencies between the code used for index AMs and the users of
reloptions.
I'm not seeing any reasonable way to split this patch in smaller ones.
--
Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/
"No necesitamos banderas
No reconocemos fronteras" (Jorge González)
В письме от пятница, 8 декабря 2023 г. 15:59:09 MSK пользователь Alvaro
Herrera написал:
Theoretically I can create patch with full options.c as it is in the patch
now, and use that code only in index AM, and keep reloption.c mostly
unchanged.This will be total mess with two different options mechanisms working in
the same time, but this might be much more easy to review. When we are
done with the first step, we can change the rest.
If this will help to finally include patch into postgres, I can do it.
Will
that help you to review?I don't think that's better, because we could create slight
inconsistencies between the code used for index AMs and the users of
reloptions.
I've written quite good regression tests for it, there should be no
inconsistency.
I'm not seeing any reasonable way to split this patch in smaller ones.
Actually me neither. Not a good one anyway.
But if somebody really need it to be split, it can be done that way.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
2024-01 Commitfest.
Hi, This patch has a CF status of "Needs Review" [1]https://commitfest.postgresql.org/46/4688/, but it seems
there were CFbot test failures last time it was run [2]https://cirrus-ci.com/task/5066432363364352. Please have a
look and post an updated version if necessary.
======
[1]: https://commitfest.postgresql.org/46/4688/
[2]: https://cirrus-ci.com/task/5066432363364352
Kind Regards,
Peter Smith.
В письме от понедельник, 22 января 2024 г. 08:24:13 MSK пользователь Peter
Smith написал:
Hi!
I've updated the patch, and it should work with modern master branch.
2024-01 Commitfest.
Hi, This patch has a CF status of "Needs Review" [1], but it seems
there were CFbot test failures last time it was run [2]. Please have a
look and post an updated version if necessary.
Do we still miss notification facility for CFbot? May be somebody have added it
since the time I've checked last time?
======
[1] https://commitfest.postgresql.org/46/4688/
[2] https://cirrus-ci.com/task/5066432363364352Kind Regards,
Peter Smith.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
v6-0001-New-options-engine.patchtext/x-patch; charset=unicode-2-0-utf-8; name=v6-0001-New-options-engine.patchDownload
From 900644f433efb502ca63c208e1fd4115efdaf2a0 Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Thu, 1 Feb 2024 09:29:25 +0300
Subject: [PATCH v6] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
src/backend/access/brin/brin.c | 46 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/meson.build | 1 +
src/backend/access/common/options.c | 1366 ++++++++++
src/backend/access/common/reloptions.c | 2234 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 19 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/bin/pg_basebackup/streamutil.c | 2 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 2 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 262 ++
src/include/access/reloptions.h | 205 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 148 +-
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
src/tools/pgindent/typedefs.list | 28 +-
47 files changed, 2592 insertions(+), 2325 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index fba3ba7771..8e6741bc74 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"
@@ -206,7 +207,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern void *blrelopt_specset(void);
+extern void blReloptionPostprocess(void *, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 6836129c90..5a7074f431 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.
*/
@@ -137,7 +97,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;
@@ -156,6 +116,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* Convert signature length from # of bits to # to words, rounding up */
+ opts->bloomLength = (opts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
+}
+
+
/*
* Fill BloomState structure for particular index.
*/
@@ -470,24 +439,37 @@ BloomInitMetapage(Relation index, ForkNumber forknum)
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/dblink/dblink.c b/contrib/dblink/dblink.c
index 19a362526d..054c1789d7 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1941,7 +1941,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 249d82d3a0..26740d3ef7 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -197,7 +197,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 630b304338..e6df6dd53a 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -71,7 +71,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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index 517f20bc37..71581fba34 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 1087a9011e..4c51f22943 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"
@@ -277,7 +276,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;
@@ -292,6 +290,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = bringetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1326,23 +1325,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.
@@ -2933,3 +2915,29 @@ brin_fill_empty_ranges(BrinBuildState *state,
blkno += state->bs_pagesPerRange;
}
}
+
+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/Makefile
index 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/meson.build b/src/backend/access/common/meson.build
index 725041a4ce..68467d5c4d 100644
--- a/src/backend/access/common/meson.build
+++ b/src/backend/access/common/meson.build
@@ -6,6 +6,7 @@ backend_sources += files(
'detoast.c',
'heaptuple.c',
'indextuple.c',
+ 'options.c',
'printsimple.c',
'printtup.c',
'relation.c',
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 0000000000..c626df3505
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1366 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 *namspace, 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 (namspace)
+ {
+ spec_set->namspace = palloc(strlen(namspace) + 1);
+ strcpy(spec_set->namspace, namspace);
+ }
+ else
+ spec_set->namspace = NULL;
+ if (num_items_expected > 0)
+ {
+ spec_set->num_allocated = num_items_expected;
+ spec_set->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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->assert_on_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->namspace = palloc(strlen(def->defnamespace) + 1);
+ strcpy(option_dst->namspace, def->defnamespace);
+ }
+ else
+ option_dst->namspace = 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_builtin(array, TEXTOID, &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 j;
+ 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 (j = 0; j < text_len; j = j + pg_mblen(text_str))
+ {
+ if (text_str[j] == '=')
+ {
+ name_len = j;
+ 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->namspace = 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_namspaces array. Items without namspace are also accepted
+ *
+ * Used while validation of syntax parser output. Error is thrown if unproper
+ * namespace is found.
+ */
+void
+optionsDefListValdateNamespaces(List *defList, char **allowed_namspaces)
+{
+ 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_namspaces[i])
+ {
+ if (strcmp(def->defnamespace,
+ allowed_namspaces[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 "namspace" namespace. If "namspace"
+ * is NULL, only namespaseless options are returned
+ */
+List *
+optionsDefListFilterNamespaces(List *defList, const char *namspace)
+{
+ ListCell *cell;
+ List *result = NIL;
+
+ foreach(cell, defList)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ if ((!namspace && !def->defnamespace) ||
+ (namspace && def->defnamespace &&
+ strcmp(namspace, 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_builtin(array, TEXTOID, &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 unparsed 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 values 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->namspace && (!spec_set->namspace ||
+ strcmp(spec_set->namspace, option->namspace) != 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter namespace \"%s\"",
+ option->namspace)));
+
+ 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->namspace)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s.%s\"",
+ option->namspace, 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 conversion.
+ */
+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;
+ if (defList)
+ data = optionsValuesToBytea(new_values, spec_set);
+ else
+ data = NULL;
+
+ spec_set->postprocess_fun(data, true);
+ if (data) 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/reloptions.c b/src/backend/access/common/reloptions.c
index 0921a736ab..fa3bf7c4de 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-2024, 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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -489,17 +106,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -507,225 +115,9 @@ static 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 *relopts, Size relopt_struct_size)
{
- relopts->options = NIL;
relopts->validators = NIL;
- relopts->relopt_struct_size = relopt_struct_size;
+ relopts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, true, 0);
}
/*
@@ -751,112 +142,6 @@ register_reloptions_validator(local_relopts *relopts, relopts_validator validato
relopts->validators = lappend(relopts->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);
}
/*
@@ -921,47 +167,8 @@ add_local_int_reloption(local_relopts *relopts, const char *name,
const char *desc, int default_val, int min_val,
int max_val, int offset)
{
- relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
- name, desc, default_val,
- min_val, max_val, 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * init_real_reloption
- * Allocate and initialize a new real reloption
- */
-static relopt_real *
-init_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption;
-
- newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
- name, desc, lockmode);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- return newoption;
-}
-
-/*
- * add_real_reloption
- * Add a new float reloption
- */
-void
-add_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption = init_real_reloption(kinds, name, desc,
- default_val, min_val,
- max_val, lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
}
/*
@@ -971,142 +178,28 @@ add_real_reloption(bits32 kinds, const char *name, const char *desc,
* '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;
- }
+add_local_real_reloption(local_relopts *relopts, const char *name,
+ const char *desc, double default_val,
+ double min_val, double max_val, int offset)
+{
+ optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
- return newoption;
}
/*
- * add_string_reloption
- * Add a new string reloption
+ * add_local_enum_reloption
+ * Add a new local enum 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.
+ * 'offset' is offset of int-typed field.
*/
void
-add_string_reloption(bits32 kinds, const char *name, const char *desc,
- const char *default_val, validate_string_relopt validator,
- LOCKMODE lockmode)
+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)
{
- relopt_string *newoption = init_string_reloption(kinds, name, desc,
- default_val,
- validator, NULL,
- lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ members, default_val, detailmsg);
}
/*
@@ -1122,247 +215,8 @@ add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset)
{
- relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- default_val,
- validator, filler,
- 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * Transform a relation options list (list of DefElem) into the text array
- * format that is kept in pg_class.reloptions, including only those options
- * that are in the passed namespace. The output values do not include the
- * namespace.
- *
- * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
- * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
- * reloptions value (possibly NULL), and we replace or remove entries
- * as needed.
- *
- * If acceptOidsOff is true, then we allow oids = false, but throw error when
- * on. This is solely needed for backwards compatibility.
- *
- * Note that this is not responsible for determining whether the options
- * are valid, but it does check that namespaces for all the options given are
- * listed in validnsps. The NULL namespace is always valid and need not be
- * explicitly listed. Passing a NULL pointer means that only the NULL
- * namespace is valid.
- *
- * Both oldOptions and the result are text arrays (or NULL for "default"),
- * but we declare them as Datums to avoid including array.h in reloptions.h.
- */
-Datum
-transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
- char *validnsps[], bool acceptOidsOff, bool isReset)
-{
- Datum result;
- ArrayBuildState *astate;
- ListCell *cell;
-
- /* no change if empty list */
- if (defList == NIL)
- return oldOptions;
-
- /* We build new array using accumArrayResult */
- astate = NULL;
-
- /* Copy any oldOptions that aren't to be replaced */
- if (PointerIsValid(DatumGetPointer(oldOptions)))
- {
- ArrayType *array = DatumGetArrayTypeP(oldOptions);
- Datum *oldoptions;
- int noldoptions;
- int i;
-
- deconstruct_array_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
-/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
- */
-List *
-untransformRelOptions(Datum options)
-{
- List *result = NIL;
- ArrayType *array;
- Datum *optiondatums;
- int noptions;
- int i;
-
- /* Nothing to do if no options */
- if (!PointerIsValid(DatumGetPointer(options)))
- return result;
-
- array = DatumGetArrayTypeP(options);
-
- deconstruct_array_builtin(array, TEXTOID, &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(p);
- }
- result = lappend(result, makeDefElem(s, val, -1));
- }
-
- return result;
+ optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, offset,
+ NULL, default_val, validator, filler);
}
/*
@@ -1379,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,
@@ -1398,544 +253,326 @@ 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_builtin(array, TEXTOID, &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.
+ * 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.
*
- * options is a reloption text array as constructed by transformRelOptions.
- * kind specifies the family of options to be processed.
+ * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
+ * since they are only used by the AV procs and don't change anything
+ * currently executing.
*
- * 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.
+ * Fillfactor can be set because it applies only to subsequent changes made to
+ * data blocks, as documented in heapio.c
*
- * 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).
+ * 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.
*
- * 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.
- */
-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++;
- }
- }
+ * 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.
+ */
+
+
+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)
+ 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);
-
- *numrelopts = numoptions;
- return reloptions;
-}
-
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
+ 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\"."));
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
+ 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 (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
+ 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);
- return values;
+ return stdrd_relopt_spec_set;
}
-/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
- */
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
-
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
- }
+static options_spec_set *heap_relopt_spec_set = NULL;
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
/*
- * 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).
+ * 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 void *
-allocateReloptStruct(Size base, relopt_value *options, int numoptions)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- Size size = base;
- int i;
-
- for (i = 0; i < numoptions; i++)
+ if (data)
{
- relopt_value *optval = &options[i];
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
-
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
-
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-
- 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)
+static options_spec_set *toast_relopt_spec_set = NULL;
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- int i;
- int offset = basesize;
+ if (toast_relopt_spec_set)
+ return toast_relopt_spec_set;
- for (i = 0; i < numoptions; i++)
- {
- int j;
- bool found = false;
+ toast_relopt_spec_set = get_stdrd_relopt_spec_set(false);
+ toast_relopt_spec_set->postprocess_fun = toast_options_postprocess;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+ return toast_relopt_spec_set;
}
/*
- * Option parser for anything that uses StdRdOptions.
+ * Do not allow to set any option on partitioned table
*/
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+static void
+partitioned_options_postprocess(void *data, bool validate)
{
- 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));
+ if (data && validate)
+ ereport(ERROR,
+ errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot specify storage parameters for a partitioned table"),
+ errhint("Specify storage parameters for its leaf partitions instead."));
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
+options_spec_set *
+get_partitioned_relopt_spec_set(void)
+{
+ if (partitioned_relopt_spec_set)
+ return partitioned_relopt_spec_set;
+ partitioned_relopt_spec_set = get_stdrd_relopt_spec_set(true);
- pfree(options);
+ /* No options for now, so spec set is empty */
+ partitioned_relopt_spec_set->postprocess_fun =
+ partitioned_options_postprocess;
- return rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1946,158 +583,165 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
+
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- foreach(lc, relopts->options)
+ if (validate && relopts->validators)
{
- local_relopt *opt = lfirst(lc);
+ int val_count = list_length(values);
+ int i;
+ option_value *val_array;
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ val_array = palloc(sizeof(option_value) * val_count);
- i++;
- }
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (validate)
foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
- if (elems)
- pfree(elems);
+ pfree(val_array);
+ }
return opts;
}
/*
- * Option parser for partitioned tables
- */
-bytea *
-partitioned_table_reloptions(Datum reloptions, bool validate)
-{
- if (validate && reloptions)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot specify storage parameters for a partitioned table"),
- errhint("Specify storage parameters for its leaf partitions instead."));
- return NULL;
-}
-
-/*
- * Option parser for views
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an options spec set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an options spec set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2107,33 +751,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.c
index 6d05e7bdcd..1c74cf60b6 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"
/*
@@ -70,7 +71,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;
@@ -85,6 +85,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -599,21 +600,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
*
@@ -700,3 +686,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.c
index 943ae91019..e169cb10a0 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -92,7 +92,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;
@@ -107,6 +106,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.c
index 0057044513..530658cdd5 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"
@@ -909,20 +909,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.
*
@@ -1099,3 +1085,39 @@ GistTranslateStratnum(Oid opclass, StrategyNumber strat)
result = OidFunctionCall1Coll(funcid, InvalidOid, UInt16GetDatum(strat));
return DatumGetUInt16(result);
}
+
+/* values from GistOptBufferingMode */
+static 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.c
index fa5b59a150..889143c307 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -89,7 +89,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;
@@ -104,6 +103,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.c
index 2c68480852..fde36e140a 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.c
index 696d79c085..64072e07a7 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"
@@ -128,7 +129,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;
@@ -143,6 +143,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1428,3 +1429,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.c
index 2e6fc14d7a..49a380dc44 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"
@@ -2159,23 +2159,6 @@ BTreeShmemInit(void)
Assert(found);
}
-bytea *
-btoptions(Datum reloptions, bool validate)
-{
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(BTOptions, fillfactor)},
- {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
- offsetof(BTOptions, vacuum_cleanup_index_scale_factor)},
- {"deduplicate_items", RELOPT_TYPE_BOOL,
- offsetof(BTOptions, deduplicate_items)}
- };
-
- return (bytea *) build_reloptions(reloptions, validate,
- RELOPT_KIND_BTREE,
- sizeof(BTOptions),
- tab, lengthof(tab));
-}
-
/*
* btproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 5b5e6e82d3..6c1ea3d936 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"
@@ -76,7 +76,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;
@@ -91,6 +90,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -733,22 +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).
@@ -1348,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.c
index 16a2fe65e6..780b499bcc 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.c
index cf61bbac1f..556cad13ed 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -112,7 +112,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 7a87626f5f..f710d8b730 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -20,6 +20,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"
@@ -562,7 +563,7 @@ DefineIndex(Oid tableId,
IndexAmRoutine *amRoutine;
bool amcanorder;
bool amissummarizing;
- amoptions_function amoptions;
+ amreloptspecset_function amreloptspecsetfn;
bool exclusion;
bool partitioned;
bool safe_index;
@@ -878,7 +879,7 @@ DefineIndex(Oid tableId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
amissummarizing = amRoutine->amsummarizing;
pfree(amRoutine);
@@ -893,10 +894,18 @@ DefineIndex(Oid tableId,
/*
* 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.
@@ -2229,8 +2238,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
Assert(attn < nkeycols);
opclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
else
opclassOptions[attn] = (Datum) 0;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 9f51696740..db19eb0c72 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"
@@ -702,7 +703,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;
@@ -850,19 +850,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);
- break;
+ {
+ /* 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)
@@ -4451,7 +4468,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);
@@ -4493,7 +4510,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.
@@ -4715,9 +4732,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8880,12 +8902,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));
@@ -15254,12 +15277,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 */
@@ -15279,38 +15303,56 @@ 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))));
+ 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),
@@ -15320,11 +15362,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;
@@ -15399,20 +15445,23 @@ 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.c
index 76ad66e51f..ae4a427bd3 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -339,10 +339,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
@@ -1058,11 +1057,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.c
index 02e1898131..7f94d9034b 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -80,7 +80,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
if (isnull)
fdw->options = NIL;
else
- fdw->options = untransformRelOptions(datum);
+ fdw->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -167,7 +167,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
if (isnull)
server->options = NIL;
else
- server->options = untransformRelOptions(datum);
+ server->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -239,7 +239,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -276,7 +276,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -309,7 +309,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -517,7 +517,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 */
@@ -615,7 +615,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 56ac4f516e..073243869d 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1887,7 +1887,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;
@@ -1951,7 +1951,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 8de821f960..39b2c5f14a 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1163,6 +1163,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;
@@ -1186,15 +1187,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);
@@ -1303,9 +1305,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index af978ccd4b..b295191d12 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"
@@ -149,7 +150,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 5ef8f2f0ca..0b2a3e15f4 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -464,7 +464,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -479,11 +479,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;
@@ -494,7 +494,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index ec63cdc8e5..ea9552ca67 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,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/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 56d1b15951..e9bf70c00c 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -763,7 +763,7 @@ AppendStringCommandOption(PQExpBuffer buf, bool use_new_option_syntax,
{
AppendPlainCommandOption(buf, use_new_option_syntax, option_name);
- if (option_value != NULL)
+ if (option_value !=NULL)
{
size_t length = strlen(option_value);
char *escaped_value = palloc(1 + 2 * length);
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 2c6c307efc..af108bbcd5 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -139,10 +139,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,
@@ -189,6 +185,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.
*/
@@ -271,7 +270,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;
@@ -283,6 +281,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amreloptspecset_function amreloptspecset; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 1c786281dd..a552003224 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -37,6 +37,8 @@ typedef struct BrinStatsData
#define BRIN_DEFAULT_PAGES_PER_RANGE 128
+#define BRIN_MIN_PAGES_PER_RANGE 1
+#define BRIN_MAX_PAGES_PER_RANGE 131072
#define BrinGetPagesPerRange(relation) \
(AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
relation->rd_rel->relam == BRIN_AM_OID), \
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index 1c7eabe604..1c8d131939 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"
/*
@@ -109,6 +110,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern void *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 51d0c74a6b..90bb631aaf 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -108,6 +108,7 @@ extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
+extern void *gingetreloptspecset(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 7b8749c8db..4579f1b5ff 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
*/
@@ -479,7 +481,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
#define GIST_MIN_FILLFACTOR 10
#define GIST_DEFAULT_FILLFACTOR 90
-extern bytea *gistoptions(Datum reloptions, bool validate);
+extern void *gistgetreloptspecset(void);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 9c7d81525b..6fa680afe3 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -474,6 +473,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 6eb162052e..4d09ad267c 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1259,7 +1259,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 0000000000..dc5654d31c
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,262 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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 *namspace; /* 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 *namspace;
+ 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 *namspace,
+ 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_namspaces);
+extern List *optionsDefListFilterNamespaces(List *defList,
+ const char *namspace);
+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.h
index 81829b8270..f80e1e840c 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-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -22,178 +17,40 @@
#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 */
+ 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 *relopts, Size relopt_struct_size);
extern void register_reloptions_validator(local_relopts *relopts,
@@ -210,7 +67,7 @@ extern void add_local_real_reloption(local_relopts *relopts, 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 *relopts, const char *name,
@@ -219,29 +76,17 @@ extern void add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, char *validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index d6a4953120..7b113d7a76 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -189,9 +189,6 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
-/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
-
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index 2e9c757b30..a4d809cc29 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -529,6 +529,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page,
extern bool spgproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
+extern void *spggetreloptspecset(void);
/* spgdoinsert.c */
extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 60d9b8f5b5..6040637c78 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -36,7 +36,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index 18185d0206..a8744fc415 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"
@@ -23,11 +23,7 @@
PG_MODULE_MAGIC;
-/* parse table for fillRelOptions */
-relopt_parse_elt di_relopt_tab[6];
-
-/* Kind of relation options for dummy index */
-relopt_kind di_relopt_kind;
+void _PG_init(void);
typedef enum DummyAmEnum
{
@@ -47,7 +43,7 @@ typedef struct DummyIndexOptions
int option_string_null_offset;
} DummyIndexOptions;
-relopt_enum_elt_def dummyAmEnumValues[] =
+static opt_enum_elt_def dummyAmEnumValues[] =
{
{"one", DUMMY_AM_ENUM_ONE},
{"two", DUMMY_AM_ENUM_TWO},
@@ -61,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.
*/
@@ -216,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.
*/
@@ -308,7 +296,6 @@ dihandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = divacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = dicostestimate;
- amroutine->amoptions = dioptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = divalidate;
@@ -322,12 +309,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.out
index 9de19b4e3f..67f0a828f9 100644
--- a/src/test/regress/expected/reloptions.out
+++ b/src/test/regress/expected/reloptions.out
@@ -175,7 +175,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
@@ -194,6 +194,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.sql
index 24fbe0b478..9b404295c0 100644
--- a/src/test/regress/sql/reloptions.sql
+++ b/src/test/regress/sql/reloptions.sql
@@ -112,6 +112,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
--
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 90b37b919c..c641002576 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3180,9 +3180,9 @@ amgettuple_function
aminitparallelscan_function
aminsert_function
ammarkpos_function
-amoptions_function
amparallelrescan_function
amproperty_function
+amreloptspecset_function
amrescan_function
amrestrpos_function
amvacuumcleanup_function
@@ -3345,6 +3345,7 @@ file_type_t
filehash_hash
filehash_iterator
filemap_t
+fill_string_option
fill_string_relopt
finalize_primnode_context
find_dependent_phvs_context
@@ -3474,7 +3475,6 @@ libpq_source
line_t
lineno_t
list_sort_comparator
-local_relopt
local_relopts
local_source
locale_t
@@ -3534,6 +3534,18 @@ oidvector
on_dsm_detach_callback
on_exit_nicely_callback
openssl_tls_init_hook_typ
+opt_enum_elt_def
+option_spec_basic
+option_spec_bool
+option_spec_enum
+option_spec_int
+option_spec_real
+option_spec_string
+option_type
+option_value
+option_value_postvalidate
+option_value_status
+options_spec_set
ossl_EVP_cipher_func
other
output_type
@@ -3648,6 +3660,7 @@ pointer
polymorphic_actuals
pos_trgm
post_parse_analyze_hook_type
+postprocess_bytea_options_function
postprocess_result_function
pqbool
pqsigfunc
@@ -3707,16 +3720,6 @@ registered_buffer
regmatch_t
regoff_t
regproc
-relopt_bool
-relopt_enum
-relopt_enum_elt_def
-relopt_gen
-relopt_int
-relopt_kind
-relopt_parse_elt
-relopt_real
-relopt_string
-relopt_type
relopt_value
relopts_validator
remoteConn
@@ -3873,6 +3876,7 @@ uuid_sortsupport_state
uuid_t
va_list
vacuumingOptions
+validate_string_option
validate_string_relopt
varatt_expanded
varattrib_1b
--
2.39.2
В письме от четверг, 1 февраля 2024 г. 09:58:32 MSK пользователь Nikolay
Shaplov написал:
I' ve updated the patch again, since the output of the
src/test/modules/test_oat_hooks test have changed.
This test began to register namespace lookups, and since options internals
have been changed much, number of lookups have been changed too. So I just
fixed the expected file.
Hi!
I've updated the patch, and it should work with modern master branch.
2024-01 Commitfest.
Hi, This patch has a CF status of "Needs Review" [1], but it seems
there were CFbot test failures last time it was run [2]. Please have a
look and post an updated version if necessary.Do we still miss notification facility for CFbot? May be somebody have added
it since the time I've checked last time?======
[1] https://commitfest.postgresql.org/46/4688/
[2] https://cirrus-ci.com/task/5066432363364352Kind Regards,
Peter Smith.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
v6-0001-New-options-engine.patchtext/x-patch; charset=unicode-2-0-utf-8; name=v6-0001-New-options-engine.patchDownload
From 24abe8aa6ea36af01d0d784048d636b651d984e9 Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Wed, 7 Feb 2024 09:36:49 +0300
Subject: [PATCH v7] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
src/backend/access/brin/brin.c | 46 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/meson.build | 1 +
src/backend/access/common/options.c | 1366 ++++++++++
src/backend/access/common/reloptions.c | 2234 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 19 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/bin/pg_basebackup/streamutil.c | 2 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 2 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 262 ++
src/include/access/reloptions.h | 205 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 148 +-
.../test_oat_hooks/expected/alter_table.out | 16 +
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
src/tools/pgindent/typedefs.list | 28 +-
48 files changed, 2608 insertions(+), 2325 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index fba3ba7771..8e6741bc74 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"
@@ -206,7 +207,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern void *blrelopt_specset(void);
+extern void blReloptionPostprocess(void *, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 6836129c90..5a7074f431 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.
*/
@@ -137,7 +97,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;
@@ -156,6 +116,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* Convert signature length from # of bits to # to words, rounding up */
+ opts->bloomLength = (opts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
+}
+
+
/*
* Fill BloomState structure for particular index.
*/
@@ -470,24 +439,37 @@ BloomInitMetapage(Relation index, ForkNumber forknum)
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/dblink/dblink.c b/contrib/dblink/dblink.c
index 19a362526d..054c1789d7 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1941,7 +1941,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 249d82d3a0..26740d3ef7 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -197,7 +197,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 630b304338..e6df6dd53a 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -71,7 +71,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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index 517f20bc37..71581fba34 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 1087a9011e..4c51f22943 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"
@@ -277,7 +276,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;
@@ -292,6 +290,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = bringetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1326,23 +1325,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.
@@ -2933,3 +2915,29 @@ brin_fill_empty_ranges(BrinBuildState *state,
blkno += state->bs_pagesPerRange;
}
}
+
+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/Makefile
index 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/meson.build b/src/backend/access/common/meson.build
index 725041a4ce..68467d5c4d 100644
--- a/src/backend/access/common/meson.build
+++ b/src/backend/access/common/meson.build
@@ -6,6 +6,7 @@ backend_sources += files(
'detoast.c',
'heaptuple.c',
'indextuple.c',
+ 'options.c',
'printsimple.c',
'printtup.c',
'relation.c',
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 0000000000..c626df3505
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1366 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 *namspace, 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 (namspace)
+ {
+ spec_set->namspace = palloc(strlen(namspace) + 1);
+ strcpy(spec_set->namspace, namspace);
+ }
+ else
+ spec_set->namspace = NULL;
+ if (num_items_expected > 0)
+ {
+ spec_set->num_allocated = num_items_expected;
+ spec_set->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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->assert_on_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->namspace = palloc(strlen(def->defnamespace) + 1);
+ strcpy(option_dst->namspace, def->defnamespace);
+ }
+ else
+ option_dst->namspace = 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_builtin(array, TEXTOID, &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 j;
+ 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 (j = 0; j < text_len; j = j + pg_mblen(text_str))
+ {
+ if (text_str[j] == '=')
+ {
+ name_len = j;
+ 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->namspace = 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_namspaces array. Items without namspace are also accepted
+ *
+ * Used while validation of syntax parser output. Error is thrown if unproper
+ * namespace is found.
+ */
+void
+optionsDefListValdateNamespaces(List *defList, char **allowed_namspaces)
+{
+ 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_namspaces[i])
+ {
+ if (strcmp(def->defnamespace,
+ allowed_namspaces[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 "namspace" namespace. If "namspace"
+ * is NULL, only namespaseless options are returned
+ */
+List *
+optionsDefListFilterNamespaces(List *defList, const char *namspace)
+{
+ ListCell *cell;
+ List *result = NIL;
+
+ foreach(cell, defList)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ if ((!namspace && !def->defnamespace) ||
+ (namspace && def->defnamespace &&
+ strcmp(namspace, 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_builtin(array, TEXTOID, &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 unparsed 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 values 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->namspace && (!spec_set->namspace ||
+ strcmp(spec_set->namspace, option->namspace) != 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter namespace \"%s\"",
+ option->namspace)));
+
+ 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->namspace)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s.%s\"",
+ option->namspace, 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 conversion.
+ */
+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;
+ if (defList)
+ data = optionsValuesToBytea(new_values, spec_set);
+ else
+ data = NULL;
+
+ spec_set->postprocess_fun(data, true);
+ if (data) 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/reloptions.c b/src/backend/access/common/reloptions.c
index 0921a736ab..fa3bf7c4de 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-2024, 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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -489,17 +106,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -507,225 +115,9 @@ static 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 *relopts, Size relopt_struct_size)
{
- relopts->options = NIL;
relopts->validators = NIL;
- relopts->relopt_struct_size = relopt_struct_size;
+ relopts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, true, 0);
}
/*
@@ -751,112 +142,6 @@ register_reloptions_validator(local_relopts *relopts, relopts_validator validato
relopts->validators = lappend(relopts->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);
}
/*
@@ -921,47 +167,8 @@ add_local_int_reloption(local_relopts *relopts, const char *name,
const char *desc, int default_val, int min_val,
int max_val, int offset)
{
- relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
- name, desc, default_val,
- min_val, max_val, 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * init_real_reloption
- * Allocate and initialize a new real reloption
- */
-static relopt_real *
-init_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption;
-
- newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
- name, desc, lockmode);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- return newoption;
-}
-
-/*
- * add_real_reloption
- * Add a new float reloption
- */
-void
-add_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption = init_real_reloption(kinds, name, desc,
- default_val, min_val,
- max_val, lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
}
/*
@@ -971,142 +178,28 @@ add_real_reloption(bits32 kinds, const char *name, const char *desc,
* '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;
- }
+add_local_real_reloption(local_relopts *relopts, const char *name,
+ const char *desc, double default_val,
+ double min_val, double max_val, int offset)
+{
+ optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
- return newoption;
}
/*
- * add_string_reloption
- * Add a new string reloption
+ * add_local_enum_reloption
+ * Add a new local enum 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.
+ * 'offset' is offset of int-typed field.
*/
void
-add_string_reloption(bits32 kinds, const char *name, const char *desc,
- const char *default_val, validate_string_relopt validator,
- LOCKMODE lockmode)
+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)
{
- relopt_string *newoption = init_string_reloption(kinds, name, desc,
- default_val,
- validator, NULL,
- lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ members, default_val, detailmsg);
}
/*
@@ -1122,247 +215,8 @@ add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset)
{
- relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- default_val,
- validator, filler,
- 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * Transform a relation options list (list of DefElem) into the text array
- * format that is kept in pg_class.reloptions, including only those options
- * that are in the passed namespace. The output values do not include the
- * namespace.
- *
- * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
- * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
- * reloptions value (possibly NULL), and we replace or remove entries
- * as needed.
- *
- * If acceptOidsOff is true, then we allow oids = false, but throw error when
- * on. This is solely needed for backwards compatibility.
- *
- * Note that this is not responsible for determining whether the options
- * are valid, but it does check that namespaces for all the options given are
- * listed in validnsps. The NULL namespace is always valid and need not be
- * explicitly listed. Passing a NULL pointer means that only the NULL
- * namespace is valid.
- *
- * Both oldOptions and the result are text arrays (or NULL for "default"),
- * but we declare them as Datums to avoid including array.h in reloptions.h.
- */
-Datum
-transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
- char *validnsps[], bool acceptOidsOff, bool isReset)
-{
- Datum result;
- ArrayBuildState *astate;
- ListCell *cell;
-
- /* no change if empty list */
- if (defList == NIL)
- return oldOptions;
-
- /* We build new array using accumArrayResult */
- astate = NULL;
-
- /* Copy any oldOptions that aren't to be replaced */
- if (PointerIsValid(DatumGetPointer(oldOptions)))
- {
- ArrayType *array = DatumGetArrayTypeP(oldOptions);
- Datum *oldoptions;
- int noldoptions;
- int i;
-
- deconstruct_array_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
-/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
- */
-List *
-untransformRelOptions(Datum options)
-{
- List *result = NIL;
- ArrayType *array;
- Datum *optiondatums;
- int noptions;
- int i;
-
- /* Nothing to do if no options */
- if (!PointerIsValid(DatumGetPointer(options)))
- return result;
-
- array = DatumGetArrayTypeP(options);
-
- deconstruct_array_builtin(array, TEXTOID, &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(p);
- }
- result = lappend(result, makeDefElem(s, val, -1));
- }
-
- return result;
+ optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, offset,
+ NULL, default_val, validator, filler);
}
/*
@@ -1379,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,
@@ -1398,544 +253,326 @@ 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_builtin(array, TEXTOID, &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.
+ * 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.
*
- * options is a reloption text array as constructed by transformRelOptions.
- * kind specifies the family of options to be processed.
+ * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
+ * since they are only used by the AV procs and don't change anything
+ * currently executing.
*
- * 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.
+ * Fillfactor can be set because it applies only to subsequent changes made to
+ * data blocks, as documented in heapio.c
*
- * 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).
+ * 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.
*
- * 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.
- */
-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++;
- }
- }
+ * 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.
+ */
+
+
+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)
+ 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);
-
- *numrelopts = numoptions;
- return reloptions;
-}
-
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
+ 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\"."));
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
+ 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 (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
+ 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);
- return values;
+ return stdrd_relopt_spec_set;
}
-/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
- */
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
-
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
- }
+static options_spec_set *heap_relopt_spec_set = NULL;
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
/*
- * 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).
+ * 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 void *
-allocateReloptStruct(Size base, relopt_value *options, int numoptions)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- Size size = base;
- int i;
-
- for (i = 0; i < numoptions; i++)
+ if (data)
{
- relopt_value *optval = &options[i];
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
-
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
-
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-
- 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)
+static options_spec_set *toast_relopt_spec_set = NULL;
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- int i;
- int offset = basesize;
+ if (toast_relopt_spec_set)
+ return toast_relopt_spec_set;
- for (i = 0; i < numoptions; i++)
- {
- int j;
- bool found = false;
+ toast_relopt_spec_set = get_stdrd_relopt_spec_set(false);
+ toast_relopt_spec_set->postprocess_fun = toast_options_postprocess;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+ return toast_relopt_spec_set;
}
/*
- * Option parser for anything that uses StdRdOptions.
+ * Do not allow to set any option on partitioned table
*/
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+static void
+partitioned_options_postprocess(void *data, bool validate)
{
- 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));
+ if (data && validate)
+ ereport(ERROR,
+ errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot specify storage parameters for a partitioned table"),
+ errhint("Specify storage parameters for its leaf partitions instead."));
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
+options_spec_set *
+get_partitioned_relopt_spec_set(void)
+{
+ if (partitioned_relopt_spec_set)
+ return partitioned_relopt_spec_set;
+ partitioned_relopt_spec_set = get_stdrd_relopt_spec_set(true);
- pfree(options);
+ /* No options for now, so spec set is empty */
+ partitioned_relopt_spec_set->postprocess_fun =
+ partitioned_options_postprocess;
- return rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1946,158 +583,165 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
+
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- foreach(lc, relopts->options)
+ if (validate && relopts->validators)
{
- local_relopt *opt = lfirst(lc);
+ int val_count = list_length(values);
+ int i;
+ option_value *val_array;
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ val_array = palloc(sizeof(option_value) * val_count);
- i++;
- }
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (validate)
foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
- if (elems)
- pfree(elems);
+ pfree(val_array);
+ }
return opts;
}
/*
- * Option parser for partitioned tables
- */
-bytea *
-partitioned_table_reloptions(Datum reloptions, bool validate)
-{
- if (validate && reloptions)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot specify storage parameters for a partitioned table"),
- errhint("Specify storage parameters for its leaf partitions instead."));
- return NULL;
-}
-
-/*
- * Option parser for views
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an options spec set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an options spec set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2107,33 +751,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.c
index 6d05e7bdcd..1c74cf60b6 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"
/*
@@ -70,7 +71,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;
@@ -85,6 +85,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -599,21 +600,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
*
@@ -700,3 +686,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.c
index 943ae91019..e169cb10a0 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -92,7 +92,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;
@@ -107,6 +106,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.c
index 0057044513..530658cdd5 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"
@@ -909,20 +909,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.
*
@@ -1099,3 +1085,39 @@ GistTranslateStratnum(Oid opclass, StrategyNumber strat)
result = OidFunctionCall1Coll(funcid, InvalidOid, UInt16GetDatum(strat));
return DatumGetUInt16(result);
}
+
+/* values from GistOptBufferingMode */
+static 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.c
index fa5b59a150..889143c307 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -89,7 +89,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;
@@ -104,6 +103,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.c
index 2c68480852..fde36e140a 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.c
index 696d79c085..64072e07a7 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"
@@ -128,7 +129,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;
@@ -143,6 +143,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1428,3 +1429,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.c
index 2e6fc14d7a..49a380dc44 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"
@@ -2159,23 +2159,6 @@ BTreeShmemInit(void)
Assert(found);
}
-bytea *
-btoptions(Datum reloptions, bool validate)
-{
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(BTOptions, fillfactor)},
- {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
- offsetof(BTOptions, vacuum_cleanup_index_scale_factor)},
- {"deduplicate_items", RELOPT_TYPE_BOOL,
- offsetof(BTOptions, deduplicate_items)}
- };
-
- return (bytea *) build_reloptions(reloptions, validate,
- RELOPT_KIND_BTREE,
- sizeof(BTOptions),
- tab, lengthof(tab));
-}
-
/*
* btproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 5b5e6e82d3..6c1ea3d936 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"
@@ -76,7 +76,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;
@@ -91,6 +90,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -733,22 +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).
@@ -1348,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.c
index 16a2fe65e6..780b499bcc 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.c
index cf61bbac1f..556cad13ed 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -112,7 +112,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 7a87626f5f..f710d8b730 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -20,6 +20,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"
@@ -562,7 +563,7 @@ DefineIndex(Oid tableId,
IndexAmRoutine *amRoutine;
bool amcanorder;
bool amissummarizing;
- amoptions_function amoptions;
+ amreloptspecset_function amreloptspecsetfn;
bool exclusion;
bool partitioned;
bool safe_index;
@@ -878,7 +879,7 @@ DefineIndex(Oid tableId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
amissummarizing = amRoutine->amsummarizing;
pfree(amRoutine);
@@ -893,10 +894,18 @@ DefineIndex(Oid tableId,
/*
* 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.
@@ -2229,8 +2238,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
Assert(attn < nkeycols);
opclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
else
opclassOptions[attn] = (Datum) 0;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 9f51696740..db19eb0c72 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"
@@ -702,7 +703,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;
@@ -850,19 +850,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);
- break;
+ {
+ /* 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)
@@ -4451,7 +4468,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);
@@ -4493,7 +4510,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.
@@ -4715,9 +4732,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8880,12 +8902,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));
@@ -15254,12 +15277,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 */
@@ -15279,38 +15303,56 @@ 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))));
+ 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),
@@ -15320,11 +15362,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;
@@ -15399,20 +15445,23 @@ 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.c
index 76ad66e51f..ae4a427bd3 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -339,10 +339,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
@@ -1058,11 +1057,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.c
index 02e1898131..7f94d9034b 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -80,7 +80,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
if (isnull)
fdw->options = NIL;
else
- fdw->options = untransformRelOptions(datum);
+ fdw->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -167,7 +167,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
if (isnull)
server->options = NIL;
else
- server->options = untransformRelOptions(datum);
+ server->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -239,7 +239,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -276,7 +276,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -309,7 +309,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -517,7 +517,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 */
@@ -615,7 +615,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 56ac4f516e..073243869d 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1887,7 +1887,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;
@@ -1951,7 +1951,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 8de821f960..39b2c5f14a 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1163,6 +1163,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;
@@ -1186,15 +1187,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);
@@ -1303,9 +1305,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index af978ccd4b..b295191d12 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"
@@ -149,7 +150,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index ac106b40e3..95b57484c2 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -464,7 +464,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -479,11 +479,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;
@@ -494,7 +494,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index ec63cdc8e5..ea9552ca67 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,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/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 56d1b15951..e9bf70c00c 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -763,7 +763,7 @@ AppendStringCommandOption(PQExpBuffer buf, bool use_new_option_syntax,
{
AppendPlainCommandOption(buf, use_new_option_syntax, option_name);
- if (option_value != NULL)
+ if (option_value !=NULL)
{
size_t length = strlen(option_value);
char *escaped_value = palloc(1 + 2 * length);
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 2c6c307efc..af108bbcd5 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -139,10 +139,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,
@@ -189,6 +185,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.
*/
@@ -271,7 +270,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;
@@ -283,6 +281,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amreloptspecset_function amreloptspecset; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 1c786281dd..a552003224 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -37,6 +37,8 @@ typedef struct BrinStatsData
#define BRIN_DEFAULT_PAGES_PER_RANGE 128
+#define BRIN_MIN_PAGES_PER_RANGE 1
+#define BRIN_MAX_PAGES_PER_RANGE 131072
#define BrinGetPagesPerRange(relation) \
(AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
relation->rd_rel->relam == BRIN_AM_OID), \
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index 1c7eabe604..1c8d131939 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"
/*
@@ -109,6 +110,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern void *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 51d0c74a6b..90bb631aaf 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -108,6 +108,7 @@ extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
+extern void *gingetreloptspecset(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 7b8749c8db..4579f1b5ff 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
*/
@@ -479,7 +481,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
#define GIST_MIN_FILLFACTOR 10
#define GIST_DEFAULT_FILLFACTOR 90
-extern bytea *gistoptions(Datum reloptions, bool validate);
+extern void *gistgetreloptspecset(void);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 9c7d81525b..6fa680afe3 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -474,6 +473,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 6eb162052e..4d09ad267c 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1259,7 +1259,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 0000000000..dc5654d31c
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,262 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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 *namspace; /* 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 *namspace;
+ 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 *namspace,
+ 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_namspaces);
+extern List *optionsDefListFilterNamespaces(List *defList,
+ const char *namspace);
+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.h
index 81829b8270..f80e1e840c 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-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -22,178 +17,40 @@
#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 */
+ 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 *relopts, Size relopt_struct_size);
extern void register_reloptions_validator(local_relopts *relopts,
@@ -210,7 +67,7 @@ extern void add_local_real_reloption(local_relopts *relopts, 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 *relopts, const char *name,
@@ -219,29 +76,17 @@ extern void add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, char *validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index d6a4953120..7b113d7a76 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -189,9 +189,6 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
-/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
-
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index 2e9c757b30..a4d809cc29 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -529,6 +529,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page,
extern bool spgproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
+extern void *spggetreloptspecset(void);
/* spgdoinsert.c */
extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 60d9b8f5b5..6040637c78 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -36,7 +36,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index 18185d0206..a8744fc415 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"
@@ -23,11 +23,7 @@
PG_MODULE_MAGIC;
-/* parse table for fillRelOptions */
-relopt_parse_elt di_relopt_tab[6];
-
-/* Kind of relation options for dummy index */
-relopt_kind di_relopt_kind;
+void _PG_init(void);
typedef enum DummyAmEnum
{
@@ -47,7 +43,7 @@ typedef struct DummyIndexOptions
int option_string_null_offset;
} DummyIndexOptions;
-relopt_enum_elt_def dummyAmEnumValues[] =
+static opt_enum_elt_def dummyAmEnumValues[] =
{
{"one", DUMMY_AM_ENUM_ONE},
{"two", DUMMY_AM_ENUM_TWO},
@@ -61,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.
*/
@@ -216,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.
*/
@@ -308,7 +296,6 @@ dihandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = divacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = dicostestimate;
- amroutine->amoptions = dioptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = divalidate;
@@ -322,12 +309,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/modules/test_oat_hooks/expected/alter_table.out b/src/test/modules/test_oat_hooks/expected/alter_table.out
index 19adb28ffb..6544332b41 100644
--- a/src/test/modules/test_oat_hooks/expected/alter_table.out
+++ b/src/test/modules/test_oat_hooks/expected/alter_table.out
@@ -80,6 +80,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -87,6 +89,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -94,6 +98,8 @@ ALTER TABLE test_oat_schema.test_oat_tab FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -101,6 +107,8 @@ ALTER TABLE test_oat_schema.test_oat_tab NO FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -109,6 +117,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -118,6 +128,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -128,6 +140,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -137,6 +151,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
index 9de19b4e3f..67f0a828f9 100644
--- a/src/test/regress/expected/reloptions.out
+++ b/src/test/regress/expected/reloptions.out
@@ -175,7 +175,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
@@ -194,6 +194,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.sql
index 24fbe0b478..9b404295c0 100644
--- a/src/test/regress/sql/reloptions.sql
+++ b/src/test/regress/sql/reloptions.sql
@@ -112,6 +112,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
--
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 91433d439b..fc28f5cac2 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3181,9 +3181,9 @@ amgettuple_function
aminitparallelscan_function
aminsert_function
ammarkpos_function
-amoptions_function
amparallelrescan_function
amproperty_function
+amreloptspecset_function
amrescan_function
amrestrpos_function
amvacuumcleanup_function
@@ -3346,6 +3346,7 @@ file_type_t
filehash_hash
filehash_iterator
filemap_t
+fill_string_option
fill_string_relopt
finalize_primnode_context
find_dependent_phvs_context
@@ -3475,7 +3476,6 @@ libpq_source
line_t
lineno_t
list_sort_comparator
-local_relopt
local_relopts
local_source
locale_t
@@ -3535,6 +3535,18 @@ oidvector
on_dsm_detach_callback
on_exit_nicely_callback
openssl_tls_init_hook_typ
+opt_enum_elt_def
+option_spec_basic
+option_spec_bool
+option_spec_enum
+option_spec_int
+option_spec_real
+option_spec_string
+option_type
+option_value
+option_value_postvalidate
+option_value_status
+options_spec_set
ossl_EVP_cipher_func
other
output_type
@@ -3649,6 +3661,7 @@ pointer
polymorphic_actuals
pos_trgm
post_parse_analyze_hook_type
+postprocess_bytea_options_function
postprocess_result_function
pqbool
pqsigfunc
@@ -3708,16 +3721,6 @@ registered_buffer
regmatch_t
regoff_t
regproc
-relopt_bool
-relopt_enum
-relopt_enum_elt_def
-relopt_gen
-relopt_int
-relopt_kind
-relopt_parse_elt
-relopt_real
-relopt_string
-relopt_type
relopt_value
relopts_validator
remoteConn
@@ -3874,6 +3877,7 @@ uuid_sortsupport_state
uuid_t
va_list
vacuumingOptions
+validate_string_option
validate_string_relopt
varatt_expanded
varattrib_1b
--
2.39.2
On Wed, Feb 7, 2024 at 2:45 PM Nikolay Shaplov <dhyan@nataraj.su> wrote:
В письме от четверг, 1 февраля 2024 г. 09:58:32 MSK пользователь Nikolay
Shaplov написал:I' ve updated the patch again, since the output of the
src/test/modules/test_oat_hooks test have changed.
This test began to register namespace lookups, and since options internals
have been changed much, number of lookups have been changed too. So I just
fixed the expected file.Hi!
I've updated the patch, and it should work with modern master branch.
you've changed the `typedef struct IndexAmRoutine`
then we also need to update doc/src/sgml/indexam.sgml.
some places you use "Spec Set", other places you use "spec set"
+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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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 *namspace; /* spec_set is used for options from this
+ * namespase */
+} options_spec_set;
here the comments, you used "spec_set".
maybe we can make it more consistent?
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;
I am slightly confused.
say
`create table a(a1 int) with (foo=bar).`
to make table a created successfully:
we need to check if variable foo is valid or not, if not then error
out immediately,
we also need to check if the variable bar is within the expected bound.
overall I am not sure about the usage of option_value_status.
using some real example demo different option_value_status usage would be great.
+ /*
+ * 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")));
+ errmsg("RESET must not include values for parameters")));
the error message seems not so good?
you declared as:
options_spec_set *get_stdrd_relopt_spec_set(bool is_for_toast);
but you used as:
options_spec_set * get_stdrd_relopt_spec_set(bool is_heap)
maybe we refactor the usage as
`options_spec_set * get_stdrd_relopt_spec_set(bool is_for_toast)`
+ 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)));
maybe
+ 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)));
+ 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)));
maybe
+ 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)));
I think the above two, change errdetail to errhint would be more appropriate.
I've attached v7, 0001 and 0002.
v7, 0001 is the rebased v6, v7, 0002 is miscellaneous minor cosmetic fix.
Attachments:
v7-0001-New-options-engine.patchtext/x-patch; charset=US-ASCII; name=v7-0001-New-options-engine.patchDownload
From 58733d469c71f4eb325531c8b8017d01c5b96473 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Mon, 3 Jun 2024 10:31:52 +0800
Subject: [PATCH v7 1/2] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
src/backend/access/brin/brin.c | 46 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/meson.build | 1 +
src/backend/access/common/options.c | 1366 ++++++++++
src/backend/access/common/reloptions.c | 2212 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 19 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/bin/pg_basebackup/streamutil.c | 2 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 2 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 262 ++
src/include/access/reloptions.h | 205 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 138 +-
.../test_oat_hooks/expected/alter_table.out | 16 +
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
src/tools/pgindent/typedefs.list | 28 +-
48 files changed, 2592 insertions(+), 2309 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index fba3ba77..8e6741bc 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"
@@ -206,7 +207,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern void *blrelopt_specset(void);
+extern void blReloptionPostprocess(void *, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 6836129c..5a7074f4 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.
*/
@@ -137,7 +97,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;
@@ -156,6 +116,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* Convert signature length from # of bits to # to words, rounding up */
+ opts->bloomLength = (opts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
+}
+
+
/*
* Fill BloomState structure for particular index.
*/
@@ -470,24 +439,37 @@ BloomInitMetapage(Relation index, ForkNumber forknum)
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/dblink/dblink.c b/contrib/dblink/dblink.c
index 75529345..e39c8cd4 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1938,7 +1938,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 249d82d3..26740d3e 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -197,7 +197,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 630b3043..e6df6dd5 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -71,7 +71,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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index 517f20bc..71581fba 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index d0f3848c..01e332c0 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"
@@ -279,7 +278,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;
@@ -294,6 +292,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = bringetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1329,23 +1328,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.
@@ -3000,3 +2982,29 @@ brin_fill_empty_ranges(BrinBuildState *state,
blkno += state->bs_pagesPerRange;
}
}
+
+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/Makefile
index e78de312..014d1b15 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/meson.build b/src/backend/access/common/meson.build
index 14090c72..0cfd4699 100644
--- a/src/backend/access/common/meson.build
+++ b/src/backend/access/common/meson.build
@@ -6,6 +6,7 @@ backend_sources += files(
'detoast.c',
'heaptuple.c',
'indextuple.c',
+ 'options.c',
'printsimple.c',
'printtup.c',
'relation.c',
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 00000000..c626df35
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1366 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 *namspace, 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 (namspace)
+ {
+ spec_set->namspace = palloc(strlen(namspace) + 1);
+ strcpy(spec_set->namspace, namspace);
+ }
+ else
+ spec_set->namspace = NULL;
+ if (num_items_expected > 0)
+ {
+ spec_set->num_allocated = num_items_expected;
+ spec_set->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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->assert_on_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->namspace = palloc(strlen(def->defnamespace) + 1);
+ strcpy(option_dst->namspace, def->defnamespace);
+ }
+ else
+ option_dst->namspace = 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_builtin(array, TEXTOID, &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 j;
+ 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 (j = 0; j < text_len; j = j + pg_mblen(text_str))
+ {
+ if (text_str[j] == '=')
+ {
+ name_len = j;
+ 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->namspace = 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_namspaces array. Items without namspace are also accepted
+ *
+ * Used while validation of syntax parser output. Error is thrown if unproper
+ * namespace is found.
+ */
+void
+optionsDefListValdateNamespaces(List *defList, char **allowed_namspaces)
+{
+ 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_namspaces[i])
+ {
+ if (strcmp(def->defnamespace,
+ allowed_namspaces[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 "namspace" namespace. If "namspace"
+ * is NULL, only namespaseless options are returned
+ */
+List *
+optionsDefListFilterNamespaces(List *defList, const char *namspace)
+{
+ ListCell *cell;
+ List *result = NIL;
+
+ foreach(cell, defList)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ if ((!namspace && !def->defnamespace) ||
+ (namspace && def->defnamespace &&
+ strcmp(namspace, 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_builtin(array, TEXTOID, &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 unparsed 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 values 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->namspace && (!spec_set->namspace ||
+ strcmp(spec_set->namspace, option->namspace) != 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter namespace \"%s\"",
+ option->namspace)));
+
+ 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->namspace)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s.%s\"",
+ option->namspace, 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 conversion.
+ */
+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;
+ if (defList)
+ data = optionsValuesToBytea(new_values, spec_set);
+ else
+ data = NULL;
+
+ spec_set->postprocess_fun(data, true);
+ if (data) 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/reloptions.c b/src/backend/access/common/reloptions.c
index d6eb5d85..f4acbdeb 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-2024, 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"
@@ -34,6 +31,7 @@
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "storage/bufmgr.h"
/*
* Contents of pg_class.reloptions
@@ -91,389 +89,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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -487,17 +104,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -505,225 +113,9 @@ static 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
@@ -733,9 +125,8 @@ add_reloption(relopt_gen *newoption)
void
init_local_reloptions(local_relopts *relopts, Size relopt_struct_size)
{
- relopts->options = NIL;
relopts->validators = NIL;
- relopts->relopt_struct_size = relopt_struct_size;
+ relopts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, true, 0);
}
/*
@@ -749,112 +140,6 @@ register_reloptions_validator(local_relopts *relopts, relopts_validator validato
relopts->validators = lappend(relopts->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
@@ -865,47 +150,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);
}
/*
@@ -919,47 +165,8 @@ add_local_int_reloption(local_relopts *relopts, const char *name,
const char *desc, int default_val, int min_val,
int max_val, int offset)
{
- relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
- name, desc, default_val,
- min_val, max_val, 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * init_real_reloption
- * Allocate and initialize a new real reloption
- */
-static relopt_real *
-init_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption;
-
- newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
- name, desc, lockmode);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- return newoption;
-}
-
-/*
- * add_real_reloption
- * Add a new float reloption
- */
-void
-add_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption = init_real_reloption(kinds, name, desc,
- default_val, min_val,
- max_val, lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
}
/*
@@ -973,57 +180,9 @@ add_local_real_reloption(local_relopts *relopts, const char *name,
const char *desc, double default_val,
double min_val, double max_val, int offset)
{
- relopt_real *newoption = init_real_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- default_val, min_val,
- max_val, 0);
+ optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
- 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);
}
/*
@@ -1034,77 +193,11 @@ add_enum_reloption(bits32 kinds, const char *name, const char *desc,
*/
void
add_local_enum_reloption(local_relopts *relopts, const char *name,
- const char *desc, relopt_enum_elt_def *members,
+ const char *desc, opt_enum_elt_def *members,
int default_val, const char *detailmsg, int offset)
{
- relopt_enum *newoption = init_enum_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- members, default_val,
- detailmsg, 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * init_string_reloption
- * Allocate and initialize a new string reloption
- */
-static relopt_string *
-init_string_reloption(bits32 kinds, const char *name, const char *desc,
- const char *default_val,
- validate_string_relopt validator,
- fill_string_relopt filler,
- LOCKMODE lockmode)
-{
- relopt_string *newoption;
-
- /* make sure the validator/default combination is sane */
- if (validator)
- (validator) (default_val);
-
- newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
- name, desc, lockmode);
- newoption->validate_cb = validator;
- newoption->fill_cb = filler;
- if (default_val)
- {
- if (kinds == RELOPT_KIND_LOCAL)
- newoption->default_val = strdup(default_val);
- else
- newoption->default_val = MemoryContextStrdup(TopMemoryContext, default_val);
- newoption->default_len = strlen(default_val);
- newoption->default_isnull = false;
- }
- else
- {
- newoption->default_val = "";
- newoption->default_len = 0;
- newoption->default_isnull = true;
- }
-
- return newoption;
-}
-
-/*
- * add_string_reloption
- * Add a new string reloption
- *
- * "validator" is an optional function pointer that can be used to test the
- * validity of the values. It must elog(ERROR) when the argument string is
- * not acceptable for the variable. Note that the default value must pass
- * the validation.
- */
-void
-add_string_reloption(bits32 kinds, const char *name, const char *desc,
- const char *default_val, validate_string_relopt validator,
- LOCKMODE lockmode)
-{
- relopt_string *newoption = init_string_reloption(kinds, name, desc,
- default_val,
- validator, NULL,
- lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ members, default_val, detailmsg);
}
/*
@@ -1120,247 +213,8 @@ add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset)
{
- relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- default_val,
- validator, filler,
- 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * Transform a relation options list (list of DefElem) into the text array
- * format that is kept in pg_class.reloptions, including only those options
- * that are in the passed namespace. The output values do not include the
- * namespace.
- *
- * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
- * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
- * reloptions value (possibly NULL), and we replace or remove entries
- * as needed.
- *
- * If acceptOidsOff is true, then we allow oids = false, but throw error when
- * on. This is solely needed for backwards compatibility.
- *
- * Note that this is not responsible for determining whether the options
- * are valid, but it does check that namespaces for all the options given are
- * listed in validnsps. The NULL namespace is always valid and need not be
- * explicitly listed. Passing a NULL pointer means that only the NULL
- * namespace is valid.
- *
- * Both oldOptions and the result are text arrays (or NULL for "default"),
- * but we declare them as Datums to avoid including array.h in reloptions.h.
- */
-Datum
-transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
- char *validnsps[], bool acceptOidsOff, bool isReset)
-{
- Datum result;
- ArrayBuildState *astate;
- ListCell *cell;
-
- /* no change if empty list */
- if (defList == NIL)
- return oldOptions;
-
- /* We build new array using accumArrayResult */
- astate = NULL;
-
- /* Copy any oldOptions that aren't to be replaced */
- if (PointerIsValid(DatumGetPointer(oldOptions)))
- {
- ArrayType *array = DatumGetArrayTypeP(oldOptions);
- Datum *oldoptions;
- int noldoptions;
- int i;
-
- deconstruct_array_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
-/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
- */
-List *
-untransformRelOptions(Datum options)
-{
- List *result = NIL;
- ArrayType *array;
- Datum *optiondatums;
- int noptions;
- int i;
-
- /* Nothing to do if no options */
- if (!PointerIsValid(DatumGetPointer(options)))
- return result;
-
- array = DatumGetArrayTypeP(options);
-
- deconstruct_array_builtin(array, TEXTOID, &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(p);
- }
- result = lappend(result, makeDefElem(s, val, -1));
- }
-
- return result;
+ optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, offset,
+ NULL, default_val, validator, filler);
}
/*
@@ -1377,12 +231,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,
@@ -1396,544 +251,326 @@ 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_builtin(array, TEXTOID, &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.
+ * 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.
*
- * options is a reloption text array as constructed by transformRelOptions.
- * kind specifies the family of options to be processed.
+ * Fillfactor can be set because it applies only to subsequent changes made to
+ * data blocks, as documented in heapio.c
*
- * 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.
+ * 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.
*
- * 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).
+ * 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.
*
- * 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.
+ * 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.
*/
-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)
+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)
+ 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)
{
- 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++;
- }
- }
+ 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);
-
- *numrelopts = numoptions;
- return reloptions;
+ 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);
+
+ return stdrd_relopt_spec_set;
}
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
+static options_spec_set *heap_relopt_spec_set = NULL;
- if (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
-
- return values;
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * 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 void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
+toast_options_postprocess(void *data, bool validate)
{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
-
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
-
- switch (option->gen->type)
+ if (data)
{
- 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;
- }
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
+ }
}
-/*
- * 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)
+static options_spec_set *toast_relopt_spec_set = NULL;
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- Size size = base;
- int i;
+ if (toast_relopt_spec_set)
+ return toast_relopt_spec_set;
- for (i = 0; i < numoptions; i++)
- {
- relopt_value *optval = &options[i];
-
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
-
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
-
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
- }
+ toast_relopt_spec_set = get_stdrd_relopt_spec_set(false);
+ toast_relopt_spec_set->postprocess_fun = toast_options_postprocess;
- return palloc0(size);
+ return toast_relopt_spec_set;
}
+
/*
- * 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.
+ * Do not allow to set any option on partitioned table
*/
static void
-fillRelOptions(void *rdopts, Size basesize,
- relopt_value *options, int numoptions,
- bool validate,
- const relopt_parse_elt *elems, int numelems)
+partitioned_options_postprocess(void *data, bool validate)
{
- int i;
- int offset = basesize;
-
- for (i = 0; i < numoptions; i++)
- {
- int j;
- bool found = false;
-
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+ if (data && validate)
+ ereport(ERROR,
+ errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot specify storage parameters for a partitioned table"),
+ errhint("Specify storage parameters for its leaf partitions instead."));
}
-/*
- * 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));
-}
+static options_spec_set *partitioned_relopt_spec_set = NULL;
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
+options_spec_set *
+get_partitioned_relopt_spec_set(void)
{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
-
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
-
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
+ if (partitioned_relopt_spec_set)
+ return partitioned_relopt_spec_set;
+ partitioned_relopt_spec_set = get_stdrd_relopt_spec_set(true);
- pfree(options);
+ /* No options for now, so spec set is empty */
+ partitioned_relopt_spec_set->postprocess_fun =
+ partitioned_options_postprocess;
- return rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1944,158 +581,165 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
- foreach(lc, relopts->options)
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
+
+ if (validate && relopts->validators)
{
- local_relopt *opt = lfirst(lc);
+ int val_count = list_length(values);
+ int i;
+ option_value *val_array;
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ val_array = palloc(sizeof(option_value) * val_count);
- i++;
- }
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (validate)
foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
- if (elems)
- pfree(elems);
+ pfree(val_array);
+ }
return opts;
}
/*
- * Option parser for partitioned tables
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-bytea *
-partitioned_table_reloptions(Datum reloptions, bool validate)
-{
- if (validate && reloptions)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot specify storage parameters for a partitioned table"),
- errhint("Specify storage parameters for its leaf partitions instead."));
- return NULL;
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Option parser for views
- */
-bytea *
-view_reloptions(Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- 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));
-}
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
-{
- StdRdOptions *rdopts;
-
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an options spec set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an options spec set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2105,33 +749,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.c
index 5747ae6a..3a67719a 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"
@@ -27,6 +27,7 @@
#include "utils/index_selfuncs.h"
#include "utils/rel.h"
#include "utils/typcache.h"
+#include "utils/guc.h"
/*
@@ -69,7 +70,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;
@@ -84,6 +84,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -598,21 +599,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
*
@@ -699,3 +685,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.c
index ed4ffa63..b5a1521a 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -91,7 +91,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;
@@ -106,6 +105,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.c
index 78e98d68..865da664 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 "common/pg_prng.h"
#include "storage/indexfsm.h"
#include "utils/float.h"
@@ -907,20 +907,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.
*
@@ -1055,3 +1041,39 @@ gistGetFakeLSN(Relation rel)
return GetFakeLSNForUnloggedRel();
}
}
+
+/* values from GistOptBufferingMode */
+static 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.c
index 01d06b7c..aae95417 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -89,7 +89,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;
@@ -104,6 +103,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.c
index 20028f5c..bb02c08a 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 "utils/lsyscache.h"
@@ -271,19 +271,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
*/
@@ -619,3 +606,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.c
index 686a3206..200189a4 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -21,6 +21,7 @@
#include "access/nbtree.h"
#include "access/relscan.h"
#include "access/xloginsert.h"
+#include "access/options.h"
#include "commands/progress.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
@@ -133,7 +134,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;
@@ -148,6 +148,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1445,3 +1446,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.c
index 05f0d23d..65658a50 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 "commands/progress.h"
#include "lib/qunique.h"
@@ -4557,23 +4557,6 @@ BTreeShmemInit(void)
Assert(found);
}
-bytea *
-btoptions(Datum reloptions, bool validate)
-{
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(BTOptions, fillfactor)},
- {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
- offsetof(BTOptions, vacuum_cleanup_index_scale_factor)},
- {"deduplicate_items", RELOPT_TYPE_BOOL,
- offsetof(BTOptions, deduplicate_items)}
- };
-
- return (bytea *) build_reloptions(reloptions, validate,
- RELOPT_KIND_BTREE,
- sizeof(BTOptions),
- tab, lengthof(tab));
-}
-
/*
* btproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 3f793125..86a48d2e 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"
@@ -76,7 +76,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;
@@ -91,6 +90,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -733,22 +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).
@@ -1348,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.c
index 62050f4d..936f640f 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -85,6 +85,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);
@@ -119,14 +120,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.c
index cf61bbac..556cad13 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -112,7 +112,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 309389e2..649aa915 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"
@@ -559,7 +560,7 @@ DefineIndex(Oid tableId,
IndexAmRoutine *amRoutine;
bool amcanorder;
bool amissummarizing;
- amoptions_function amoptions;
+ amreloptspecset_function amreloptspecsetfn;
bool partitioned;
bool safe_index;
Datum reloptions;
@@ -870,7 +871,7 @@ DefineIndex(Oid tableId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
amissummarizing = amRoutine->amsummarizing;
pfree(amRoutine);
@@ -885,10 +886,18 @@ DefineIndex(Oid tableId,
/*
* 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.
@@ -2195,8 +2204,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
Assert(attn < nkeycols);
opclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
else
opclassOptions[attn] = (Datum) 0;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7b6c69b7..e145bf79 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"
@@ -699,7 +700,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;
@@ -842,19 +842,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);
- break;
+ {
+ /* 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)
@@ -4366,7 +4383,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);
@@ -4408,7 +4425,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.
@@ -4629,9 +4646,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8720,12 +8742,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));
@@ -14857,12 +14880,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 */
@@ -14882,38 +14906,56 @@ 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))));
+ 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),
@@ -14923,11 +14965,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;
@@ -15002,20 +15048,23 @@ 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.c
index 113b4807..01aace1f 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -333,10 +333,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
@@ -1052,11 +1051,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.c
index f4f35728..307195a1 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);
@@ -238,7 +238,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -275,7 +275,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -308,7 +308,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -516,7 +516,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 */
@@ -614,7 +614,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index d5c2b2ff..d0df5822 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1726,7 +1726,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;
@@ -1790,7 +1790,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index fa66b801..bafa4819 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1156,6 +1156,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;
@@ -1179,15 +1180,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);
@@ -1296,9 +1298,12 @@ ProcessUtilitySlow(ParseState *pstate,
* lock on (for example) a relation on which we have no
* permissions.
*/
- lockmode = AlterTableGetLockLevel(atstmt->cmds);
- relid = AlterTableLookupRelation(atstmt, lockmode);
-
+ relid = AlterTableLookupRelation(atstmt, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index af978ccd..b295191d 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"
@@ -149,7 +150,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index cc9b0c65..761811d9 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -463,7 +463,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -478,11 +478,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;
@@ -493,7 +493,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index ec63cdc8..ea9552ca 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,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/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index feee451d..a611f9da 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -832,7 +832,7 @@ AppendStringCommandOption(PQExpBuffer buf, bool use_new_option_syntax,
{
AppendPlainCommandOption(buf, use_new_option_syntax, option_name);
- if (option_value != NULL)
+ if (option_value !=NULL)
{
size_t length = strlen(option_value);
char *escaped_value = palloc(1 + 2 * length);
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index f25c9d58..0ef48df4 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -140,10 +140,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,
@@ -190,6 +186,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.
*/
@@ -272,7 +271,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;
@@ -284,6 +282,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amreloptspecset_function amreloptspecset; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 1c786281..a5520032 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -37,6 +37,8 @@ typedef struct BrinStatsData
#define BRIN_DEFAULT_PAGES_PER_RANGE 128
+#define BRIN_MIN_PAGES_PER_RANGE 1
+#define BRIN_MAX_PAGES_PER_RANGE 131072
#define BrinGetPagesPerRange(relation) \
(AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
relation->rd_rel->relam == BRIN_AM_OID), \
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index a5a97726..fd99dfae 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"
/*
@@ -109,6 +110,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern void *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 3013a44b..5ba3a757 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -109,6 +109,7 @@ extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
+extern void *gingetreloptspecset(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 7b8749c8..4579f1b5 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
*/
@@ -479,7 +481,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
#define GIST_MIN_FILLFACTOR 10
#define GIST_DEFAULT_FILLFACTOR 90
-extern bytea *gistoptions(Datum reloptions, bool validate);
+extern void *gistgetreloptspecset(void);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 9c7d8152..6fa680af 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -474,6 +473,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 74930433..0ec72c7d 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1298,7 +1298,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 00000000..dc5654d3
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,262 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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 *namspace; /* 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 *namspace;
+ 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 *namspace,
+ 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_namspaces);
+extern List *optionsDefListFilterNamespaces(List *defList,
+ const char *namspace);
+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.h
index 81829b82..f80e1e84 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-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -22,178 +17,40 @@
#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 */
+ 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 *relopts, Size relopt_struct_size);
extern void register_reloptions_validator(local_relopts *relopts,
@@ -210,7 +67,7 @@ extern void add_local_real_reloption(local_relopts *relopts, 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 *relopts, const char *name,
@@ -219,29 +76,17 @@ extern void add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, char *validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index d6a49531..7b113d7a 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -189,9 +189,6 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
-/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
-
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index 2e9c757b..a4d809cc 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -529,6 +529,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page,
extern bool spgproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
+extern void *spggetreloptspecset(void);
/* spgdoinsert.c */
extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 85cbad3d..1c7bb7f9 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -36,7 +36,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index 18185d02..a8744fc4 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"
@@ -23,11 +23,7 @@
PG_MODULE_MAGIC;
-/* parse table for fillRelOptions */
-relopt_parse_elt di_relopt_tab[6];
-
-/* Kind of relation options for dummy index */
-relopt_kind di_relopt_kind;
+void _PG_init(void);
typedef enum DummyAmEnum
{
@@ -47,7 +43,7 @@ typedef struct DummyIndexOptions
int option_string_null_offset;
} DummyIndexOptions;
-relopt_enum_elt_def dummyAmEnumValues[] =
+static opt_enum_elt_def dummyAmEnumValues[] =
{
{"one", DUMMY_AM_ENUM_ONE},
{"two", DUMMY_AM_ENUM_TWO},
@@ -61,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();
+ if (di_relopt_specset)
+ return di_relopt_specset;
- 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);
+ di_relopt_specset = allocateOptionsSpecSet(NULL,
+ sizeof(DummyIndexOptions), false, 6);
- 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);
+ optionsSpecSetAddInt(
+ di_relopt_specset, "option_int",
+ "Integer option for dummy_index_am",
+ AccessExclusiveLock,
+ offsetof(DummyIndexOptions, option_int), NULL,
+ 10, -10, 100
+ );
- 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);
+ optionsSpecSetAddReal(
+ di_relopt_specset, "option_real",
+ "Real option for dummy_index_am",
+ AccessExclusiveLock,
+ offsetof(DummyIndexOptions, option_real), NULL,
+ 3.1415, -10, 100
+ );
- 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);
+ 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.
*/
@@ -216,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.
*/
@@ -308,7 +296,6 @@ dihandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = divacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = dicostestimate;
- amroutine->amoptions = dioptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = divalidate;
@@ -322,12 +309,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/modules/test_oat_hooks/expected/alter_table.out b/src/test/modules/test_oat_hooks/expected/alter_table.out
index 8cbacca2..8319cd06 100644
--- a/src/test/modules/test_oat_hooks/expected/alter_table.out
+++ b/src/test/modules/test_oat_hooks/expected/alter_table.out
@@ -82,6 +82,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -89,6 +91,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -96,6 +100,8 @@ ALTER TABLE test_oat_schema.test_oat_tab FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -103,6 +109,8 @@ ALTER TABLE test_oat_schema.test_oat_tab NO FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -111,6 +119,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -120,6 +130,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -130,6 +142,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -139,6 +153,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
index 9de19b4e..67f0a828 100644
--- a/src/test/regress/expected/reloptions.out
+++ b/src/test/regress/expected/reloptions.out
@@ -175,7 +175,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
@@ -194,6 +194,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.sql
index 24fbe0b4..9b404295 100644
--- a/src/test/regress/sql/reloptions.sql
+++ b/src/test/regress/sql/reloptions.sql
@@ -112,6 +112,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
--
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index d427a1c1..d3a7c787 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3269,9 +3269,9 @@ aminitparallelscan_function
aminsert_function
aminsertcleanup_function
ammarkpos_function
-amoptions_function
amparallelrescan_function
amproperty_function
+amreloptspecset_function
amrescan_function
amrestrpos_function
amvacuumcleanup_function
@@ -3447,6 +3447,7 @@ file_type_t
filehash_hash
filehash_iterator
filemap_t
+fill_string_option
fill_string_relopt
finalize_primnode_context
find_dependent_phvs_context
@@ -3581,7 +3582,6 @@ libpq_source
line_t
lineno_t
list_sort_comparator
-local_relopt
local_relopts
local_source
local_ts_iter
@@ -3645,6 +3645,18 @@ oidvector
on_dsm_detach_callback
on_exit_nicely_callback
openssl_tls_init_hook_typ
+opt_enum_elt_def
+option_spec_basic
+option_spec_bool
+option_spec_enum
+option_spec_int
+option_spec_real
+option_spec_string
+option_type
+option_value
+option_value_postvalidate
+option_value_status
+options_spec_set
ossl_EVP_cipher_func
other
output_type
@@ -3763,6 +3775,7 @@ pointer
polymorphic_actuals
pos_trgm
post_parse_analyze_hook_type
+postprocess_bytea_options_function
postprocess_result_function
pqbool
pqsigfunc
@@ -3822,16 +3835,6 @@ registered_buffer
regmatch_t
regoff_t
regproc
-relopt_bool
-relopt_enum
-relopt_enum_elt_def
-relopt_gen
-relopt_int
-relopt_kind
-relopt_parse_elt
-relopt_real
-relopt_string
-relopt_type
relopt_value
relopts_validator
remoteConn
@@ -3998,6 +4001,7 @@ uuid_sortsupport_state
uuid_t
va_list
vacuumingOptions
+validate_string_option
validate_string_relopt
varatt_expanded
varattrib_1b
base-commit: 8fea1bd5411b793697a4c9087c403887e050c4ac
--
2.34.1
v7-0002-miscellaneous-changes-for-new-options-engine-feat.patchtext/x-patch; charset=US-ASCII; name=v7-0002-miscellaneous-changes-for-new-options-engine-feat.patchDownload
From 76eb68eb1cc22578db57e8bf09190084dc586d3c Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Mon, 3 Jun 2024 10:37:48 +0800
Subject: [PATCH v7 2/2] miscellaneous changes for new options engine feat.
fix typo,
fix comments,
add trail comma for the last enum elemnt in the enum struct.
---
src/backend/access/common/options.c | 20 ++++++++++----------
src/include/access/options.h | 6 +++---
2 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
index c626df35..e5399747 100644
--- a/src/backend/access/common/options.c
+++ b/src/backend/access/common/options.c
@@ -5,7 +5,7 @@
* to process relation options (reloptions), attribute options, opclass
* options, etc.
*
- * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
@@ -42,7 +42,7 @@
* optionsSpecSetAdd* functions that are used for adding Option Specs items to
* a Set.
*
- * NOTE: we choose therm "specification" instead of "definition" because therm
+ * NOTE: we choose term "specification" instead of "definition" because term
* "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.
@@ -85,7 +85,7 @@
* 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
+ * representation can be obtained from Value List using optionsValuesToBytea
* function, and can't be converted back.
*/
@@ -98,13 +98,13 @@
* 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
+ * parts. First goes a C-structure that stores fixed length values. 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.
+ * String_pointer = Bytea_head_pointer + offset.
*/
static option_spec_basic *allocateOptionSpec(int type, const char *name,
@@ -133,7 +133,7 @@ static List *optionsMergeOptionValues(List *old_options, List *new_options);
* 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
+ * bytea_size - 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
@@ -215,7 +215,7 @@ allocateOptionSpec(int type, const char *name, const char *desc,
size = sizeof(option_spec_string);
break;
default:
- elog(ERROR, "unsupported reloption type %d", type);
+ elog(ERROR, "unsupported reloption type %d", (int) type);
return NULL; /* keep compiler quiet */
}
@@ -239,7 +239,7 @@ allocateOptionSpec(int type, const char *name, const char *desc,
/*
* optionSpecSetAddItem
- * Adds pre-created Option Specification objec to the Spec Set
+ * Adds pre-created Option Specification object to the Spec Set
*/
static void
optionSpecSetAddItem(option_spec_basic *newoption,
@@ -1036,7 +1036,7 @@ parse_one_option(option_value *option, bool validate)
}
break;
default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
+ elog(ERROR, "unsupported reloption type %d", (int) option->gen->type);
parsed = true; /* quiet compiler */
break;
}
@@ -1241,7 +1241,7 @@ optionsValuesToBytea(List *options, options_spec_set *spec_set)
break;
default:
elog(ERROR, "unsupported reloption type %d",
- opt_spec->type);
+ (int) opt_spec->type);
break;
}
}
diff --git a/src/include/access/options.h b/src/include/access/options.h
index dc5654d3..40a408c3 100644
--- a/src/include/access/options.h
+++ b/src/include/access/options.h
@@ -9,7 +9,7 @@
* 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) 1996-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
@@ -32,7 +32,7 @@ typedef enum option_type
OPTION_TYPE_INT,
OPTION_TYPE_REAL,
OPTION_TYPE_ENUM,
- OPTION_TYPE_STRING
+ OPTION_TYPE_STRING,
} option_type;
typedef enum option_value_status
@@ -42,7 +42,7 @@ typedef enum option_value_status
* 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_FOR_RESET, /* This option came from ALTER xxx RESET */
} option_value_status;
/*
--
2.34.1
В письме от понедельник, 3 июня 2024 г. 10:52:02 MSK пользователь jian he
написал:
Hi!
Thank you for giving attention to my patch, and sorry, it takes me some time
to deal with such things.
By the way, what is your further intentions concerning this patch? Should I
add you as a reviewer, or you are just passing by? Or add you as second
author, if you are planning to suggest big changes
So I tried to fix all issues you've pointed out:
you've changed the `typedef struct IndexAmRoutine`
then we also need to update doc/src/sgml/indexam.sgml.
Oh. You are right. I've fixed that. The description I've created is quite
short. I do not know what else I can say there without diving deep into
implementation details. Do you have any ideas what can be added there?
some places you use "Spec Set", other places you use "spec set"
...
here the comments, you used "spec_set".
maybe we can make it more consistent?
This sounds reasonable. I've changed it to Spec Set and Options Spec Set
wherever I found alternative spelling.
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;
overall I am not sure about the usage of option_value_status.
using some real example demo different option_value_status usage would be
great.
I've added explanatory comment before option_value_status enum explaining what
is what. Hope it will make things more clear.
If not me know and we will try to find out what comments should be added
+ errmsg("RESET must not include values for parameters")));
the error message seems not so good?
eh... It does seem to be not good, but I've copied it from original code
https://github.com/postgres/postgres/blob/
00ac25a3c365004821e819653c3307acd3294818/src/backend/access/common/
reloptions.c#L1231
I would not dare changing it. I've already changed to many things here.
you declared as:
options_spec_set *get_stdrd_relopt_spec_set(bool is_for_toast);
but you used as:
options_spec_set * get_stdrd_relopt_spec_set(bool is_heap)maybe we refactor the usage as
`options_spec_set * get_stdrd_relopt_spec_set(bool is_for_toast)`
My bad.
I've changed is_for_toast to is_heap once, and I guess I've missed function
declaration... Fixed it to be consistent.
+ 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)));
.....
I think the above two, change errdetail to errhint would be more
appropriate.
Same as above... I've just copied error messages from the original code.
Fixing them better go as a separate patch I guess...
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
v8-0001-New-options-engine.patchtext/x-patch; charset=unicode-2-0-utf-8; name=v8-0001-New-options-engine.patchDownload
From 91bf99b9dfc03847bf5384713c2eee240de6d278 Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Sun, 16 Jun 2024 16:27:16 +0300
Subject: [PATCH v8 1/1] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
doc/src/sgml/indexam.sgml | 25 +-
src/backend/access/brin/brin.c | 46 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/meson.build | 1 +
src/backend/access/common/options.c | 1366 ++++++++++
src/backend/access/common/reloptions.c | 2234 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 19 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/bin/pg_basebackup/streamutil.c | 2 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 2 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 274 ++
src/include/access/reloptions.h | 205 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 148 +-
.../test_oat_hooks/expected/alter_table.out | 16 +
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
48 files changed, 2609 insertions(+), 2333 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index fba3ba7771..8e6741bc74 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"
@@ -206,7 +207,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern void *blrelopt_specset(void);
+extern void blReloptionPostprocess(void *, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 6836129c90..5a7074f431 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.
*/
@@ -137,7 +97,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;
@@ -156,6 +116,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* Convert signature length from # of bits to # to words, rounding up */
+ opts->bloomLength = (opts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
+}
+
+
/*
* Fill BloomState structure for particular index.
*/
@@ -470,24 +439,37 @@ BloomInitMetapage(Relation index, ForkNumber forknum)
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/dblink/dblink.c b/contrib/dblink/dblink.c
index 755293456f..e39c8cd49a 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1938,7 +1938,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 249d82d3a0..26740d3ef7 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -197,7 +197,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 630b304338..e6df6dd53a 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -71,7 +71,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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index 517f20bc37..71581fba34 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
index e3c1539a1e..31b7d940e7 100644
--- a/doc/src/sgml/indexam.sgml
+++ b/doc/src/sgml/indexam.sgml
@@ -480,27 +480,12 @@ amcostestimate (PlannerInfo *root,
<para>
<programlisting>
-bytea *
-amoptions (ArrayType *reloptions,
- bool validate);
+void *
+amreloptspecset(void);
</programlisting>
- Parse and validate the reloptions array for an index. This is called only
- when a non-null reloptions array exists for the index.
- <parameter>reloptions</parameter> is a <type>text</type> array containing entries of the
- form <replaceable>name</replaceable><literal>=</literal><replaceable>value</replaceable>.
- The function should construct a <type>bytea</type> value, which will be copied
- into the <structfield>rd_options</structfield> field of the index's relcache entry.
- The data contents of the <type>bytea</type> value are open for the access
- method to define; most of the standard access methods use struct
- <structname>StdRdOptions</structname>.
- When <parameter>validate</parameter> is true, the function should report a suitable
- error message if any of the options are unrecognized or have invalid
- values; when <parameter>validate</parameter> is false, invalid entries should be
- silently ignored. (<parameter>validate</parameter> is false when loading options
- already stored in <structname>pg_catalog</structname>; an invalid entry could only
- be found if the access method has changed its rules for options, and in
- that case ignoring obsolete entries is appropriate.)
- It is OK to return NULL if default behavior is wanted.
+ Returns pointer to Options Spec Set that defines options available for an
+ index relation. This Spec Set will be used for parsing and validating index
+ relation options.
</para>
<para>
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index d0f3848c95..01e332c033 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"
@@ -279,7 +278,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;
@@ -294,6 +292,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = bringetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1329,23 +1328,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.
@@ -3000,3 +2982,29 @@ brin_fill_empty_ranges(BrinBuildState *state,
blkno += state->bs_pagesPerRange;
}
}
+
+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/Makefile
index e78de31265..014d1b1562 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/meson.build b/src/backend/access/common/meson.build
index 14090c728f..0cfd469910 100644
--- a/src/backend/access/common/meson.build
+++ b/src/backend/access/common/meson.build
@@ -6,6 +6,7 @@ backend_sources += files(
'detoast.c',
'heaptuple.c',
'indextuple.c',
+ 'options.c',
'printsimple.c',
'printtup.c',
'relation.c',
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 0000000000..8cc790365b
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1366 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2024, 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 term "specification" instead of "definition" because term
+ * "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 Value 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 values. 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 + offset.
+ */
+
+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 Options 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.
+ *
+ * bytea_size - 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 *namspace, 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 (namspace)
+ {
+ spec_set->namspace = palloc(strlen(namspace) + 1);
+ strcpy(spec_set->namspace, namspace);
+ }
+ else
+ spec_set->namspace = NULL;
+ if (num_items_expected > 0)
+ {
+ spec_set->num_allocated = num_items_expected;
+ spec_set->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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", (int) 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 object 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->assert_on_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->namspace = palloc(strlen(def->defnamespace) + 1);
+ strcpy(option_dst->namspace, def->defnamespace);
+ }
+ else
+ option_dst->namspace = 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_builtin(array, TEXTOID, &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 j;
+ 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 (j = 0; j < text_len; j = j + pg_mblen(text_str))
+ {
+ if (text_str[j] == '=')
+ {
+ name_len = j;
+ 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->namspace = 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_namspaces array. Items without namspace are also accepted
+ *
+ * Used while validation of syntax parser output. Error is thrown if unproper
+ * namespace is found.
+ */
+void
+optionsDefListValdateNamespaces(List *defList, char **allowed_namspaces)
+{
+ 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_namspaces[i])
+ {
+ if (strcmp(def->defnamespace,
+ allowed_namspaces[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 "namspace" namespace. If "namspace"
+ * is NULL, only namespaseless options are returned
+ */
+List *
+optionsDefListFilterNamespaces(List *defList, const char *namspace)
+{
+ ListCell *cell;
+ List *result = NIL;
+
+ foreach(cell, defList)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ if ((!namspace && !def->defnamespace) ||
+ (namspace && def->defnamespace &&
+ strcmp(namspace, 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_builtin(array, TEXTOID, &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 unparsed 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 values 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->namspace && (!spec_set->namspace ||
+ strcmp(spec_set->namspace, option->namspace) != 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter namespace \"%s\"",
+ option->namspace)));
+
+ 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->namspace)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s.%s\"",
+ option->namspace, 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", (int) 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",
+ (int) 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 conversion.
+ */
+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;
+ if (defList)
+ data = optionsValuesToBytea(new_values, spec_set);
+ else
+ data = NULL;
+
+ spec_set->postprocess_fun(data, true);
+ if (data) 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/reloptions.c b/src/backend/access/common/reloptions.c
index d6eb5d8559..7ec05df7b9 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-2024, 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"
@@ -34,6 +31,7 @@
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "storage/bufmgr.h"
/*
* Contents of pg_class.reloptions
@@ -91,389 +89,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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -487,17 +104,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -505,225 +113,9 @@ static 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_heap);
+void oid_postvalidate(option_value *value);
/*
* init_local_reloptions
@@ -733,9 +125,8 @@ add_reloption(relopt_gen *newoption)
void
init_local_reloptions(local_relopts *relopts, Size relopt_struct_size)
{
- relopts->options = NIL;
relopts->validators = NIL;
- relopts->relopt_struct_size = relopt_struct_size;
+ relopts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, true, 0);
}
/*
@@ -749,112 +140,6 @@ register_reloptions_validator(local_relopts *relopts, relopts_validator validato
relopts->validators = lappend(relopts->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
@@ -865,47 +150,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);
}
/*
@@ -919,47 +165,8 @@ add_local_int_reloption(local_relopts *relopts, const char *name,
const char *desc, int default_val, int min_val,
int max_val, int offset)
{
- relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
- name, desc, default_val,
- min_val, max_val, 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * init_real_reloption
- * Allocate and initialize a new real reloption
- */
-static relopt_real *
-init_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption;
-
- newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
- name, desc, lockmode);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- return newoption;
-}
-
-/*
- * add_real_reloption
- * Add a new float reloption
- */
-void
-add_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption = init_real_reloption(kinds, name, desc,
- default_val, min_val,
- max_val, lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
}
/*
@@ -969,142 +176,28 @@ add_real_reloption(bits32 kinds, const char *name, const char *desc,
* '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;
- }
+add_local_real_reloption(local_relopts *relopts, const char *name,
+ const char *desc, double default_val,
+ double min_val, double max_val, int offset)
+{
+ optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
- return newoption;
}
/*
- * add_string_reloption
- * Add a new string reloption
+ * add_local_enum_reloption
+ * Add a new local enum 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.
+ * 'offset' is offset of int-typed field.
*/
void
-add_string_reloption(bits32 kinds, const char *name, const char *desc,
- const char *default_val, validate_string_relopt validator,
- LOCKMODE lockmode)
+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)
{
- relopt_string *newoption = init_string_reloption(kinds, name, desc,
- default_val,
- validator, NULL,
- lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ members, default_val, detailmsg);
}
/*
@@ -1120,247 +213,8 @@ add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset)
{
- relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- default_val,
- validator, filler,
- 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * Transform a relation options list (list of DefElem) into the text array
- * format that is kept in pg_class.reloptions, including only those options
- * that are in the passed namespace. The output values do not include the
- * namespace.
- *
- * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
- * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
- * reloptions value (possibly NULL), and we replace or remove entries
- * as needed.
- *
- * If acceptOidsOff is true, then we allow oids = false, but throw error when
- * on. This is solely needed for backwards compatibility.
- *
- * Note that this is not responsible for determining whether the options
- * are valid, but it does check that namespaces for all the options given are
- * listed in validnsps. The NULL namespace is always valid and need not be
- * explicitly listed. Passing a NULL pointer means that only the NULL
- * namespace is valid.
- *
- * Both oldOptions and the result are text arrays (or NULL for "default"),
- * but we declare them as Datums to avoid including array.h in reloptions.h.
- */
-Datum
-transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
- char *validnsps[], bool acceptOidsOff, bool isReset)
-{
- Datum result;
- ArrayBuildState *astate;
- ListCell *cell;
-
- /* no change if empty list */
- if (defList == NIL)
- return oldOptions;
-
- /* We build new array using accumArrayResult */
- astate = NULL;
-
- /* Copy any oldOptions that aren't to be replaced */
- if (PointerIsValid(DatumGetPointer(oldOptions)))
- {
- ArrayType *array = DatumGetArrayTypeP(oldOptions);
- Datum *oldoptions;
- int noldoptions;
- int i;
-
- deconstruct_array_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
-/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
- */
-List *
-untransformRelOptions(Datum options)
-{
- List *result = NIL;
- ArrayType *array;
- Datum *optiondatums;
- int noptions;
- int i;
-
- /* Nothing to do if no options */
- if (!PointerIsValid(DatumGetPointer(options)))
- return result;
-
- array = DatumGetArrayTypeP(options);
-
- deconstruct_array_builtin(array, TEXTOID, &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(p);
- }
- result = lappend(result, makeDefElem(s, val, -1));
- }
-
- return result;
+ optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, offset,
+ NULL, default_val, validator, filler);
}
/*
@@ -1377,12 +231,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,
@@ -1396,544 +251,326 @@ 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_builtin(array, TEXTOID, &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.
+ * 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.
*
- * options is a reloption text array as constructed by transformRelOptions.
- * kind specifies the family of options to be processed.
+ * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
+ * since they are only used by the AV procs and don't change anything
+ * currently executing.
*
- * 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.
+ * Fillfactor can be set because it applies only to subsequent changes made to
+ * data blocks, as documented in heapio.c
*
- * 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).
+ * 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.
*
- * 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.
- */
-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++;
- }
- }
+ * 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.
+ */
+
+
+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)
+ 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);
-
- *numrelopts = numoptions;
- return reloptions;
-}
-
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
+ 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\"."));
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
+ 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 (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
+ 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);
- return values;
+ return stdrd_relopt_spec_set;
}
-/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
- */
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
-
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
- }
+static options_spec_set *heap_relopt_spec_set = NULL;
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
/*
- * 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).
+ * 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 void *
-allocateReloptStruct(Size base, relopt_value *options, int numoptions)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- Size size = base;
- int i;
-
- for (i = 0; i < numoptions; i++)
+ if (data)
{
- relopt_value *optval = &options[i];
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
-
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
-
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-
- 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)
+static options_spec_set *toast_relopt_spec_set = NULL;
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- int i;
- int offset = basesize;
+ if (toast_relopt_spec_set)
+ return toast_relopt_spec_set;
- for (i = 0; i < numoptions; i++)
- {
- int j;
- bool found = false;
+ toast_relopt_spec_set = get_stdrd_relopt_spec_set(false);
+ toast_relopt_spec_set->postprocess_fun = toast_options_postprocess;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+ return toast_relopt_spec_set;
}
/*
- * Option parser for anything that uses StdRdOptions.
+ * Do not allow to set any option on partitioned table
*/
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+static void
+partitioned_options_postprocess(void *data, bool validate)
{
- 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));
+ if (data && validate)
+ ereport(ERROR,
+ errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot specify storage parameters for a partitioned table"),
+ errhint("Specify storage parameters for its leaf partitions instead."));
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
+options_spec_set *
+get_partitioned_relopt_spec_set(void)
+{
+ if (partitioned_relopt_spec_set)
+ return partitioned_relopt_spec_set;
+ partitioned_relopt_spec_set = get_stdrd_relopt_spec_set(true);
- pfree(options);
+ /* No options for now, so Spec Set is empty */
+ partitioned_relopt_spec_set->postprocess_fun =
+ partitioned_options_postprocess;
- return rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1944,158 +581,165 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
+
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- foreach(lc, relopts->options)
+ if (validate && relopts->validators)
{
- local_relopt *opt = lfirst(lc);
+ int val_count = list_length(values);
+ int i;
+ option_value *val_array;
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ val_array = palloc(sizeof(option_value) * val_count);
- i++;
- }
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (validate)
foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
- if (elems)
- pfree(elems);
+ pfree(val_array);
+ }
return opts;
}
/*
- * Option parser for partitioned tables
- */
-bytea *
-partitioned_table_reloptions(Datum reloptions, bool validate)
-{
- if (validate && reloptions)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot specify storage parameters for a partitioned table"),
- errhint("Specify storage parameters for its leaf partitions instead."));
- return NULL;
-}
-
-/*
- * Option parser for views
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an Options Spec Set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an Options Spec Set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2105,33 +749,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.c
index 5747ae6a4c..3a67719ab9 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"
@@ -27,6 +27,7 @@
#include "utils/index_selfuncs.h"
#include "utils/rel.h"
#include "utils/typcache.h"
+#include "utils/guc.h"
/*
@@ -69,7 +70,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;
@@ -84,6 +84,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -598,21 +599,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
*
@@ -699,3 +685,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.c
index ed4ffa63a7..b5a1521a83 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -91,7 +91,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;
@@ -106,6 +105,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.c
index 78e98d68b1..865da66455 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 "common/pg_prng.h"
#include "storage/indexfsm.h"
#include "utils/float.h"
@@ -907,20 +907,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.
*
@@ -1055,3 +1041,39 @@ gistGetFakeLSN(Relation rel)
return GetFakeLSNForUnloggedRel();
}
}
+
+/* values from GistOptBufferingMode */
+static 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.c
index 01d06b7c32..aae9541762 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -89,7 +89,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;
@@ -104,6 +103,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.c
index 20028f5cd1..bb02c08a3d 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 "utils/lsyscache.h"
@@ -271,19 +271,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
*/
@@ -619,3 +606,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.c
index 686a3206f7..200189a4d0 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -21,6 +21,7 @@
#include "access/nbtree.h"
#include "access/relscan.h"
#include "access/xloginsert.h"
+#include "access/options.h"
#include "commands/progress.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
@@ -133,7 +134,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;
@@ -148,6 +148,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1445,3 +1446,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.c
index 05f0d23dc0..65658a5023 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 "commands/progress.h"
#include "lib/qunique.h"
@@ -4557,23 +4557,6 @@ BTreeShmemInit(void)
Assert(found);
}
-bytea *
-btoptions(Datum reloptions, bool validate)
-{
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(BTOptions, fillfactor)},
- {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
- offsetof(BTOptions, vacuum_cleanup_index_scale_factor)},
- {"deduplicate_items", RELOPT_TYPE_BOOL,
- offsetof(BTOptions, deduplicate_items)}
- };
-
- return (bytea *) build_reloptions(reloptions, validate,
- RELOPT_KIND_BTREE,
- sizeof(BTOptions),
- tab, lengthof(tab));
-}
-
/*
* btproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 3f793125f7..86a48d2eff 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"
@@ -76,7 +76,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;
@@ -91,6 +90,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -733,22 +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).
@@ -1348,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.c
index 62050f4dc5..936f640fdf 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -85,6 +85,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);
@@ -119,14 +120,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.c
index cf61bbac1f..556cad13ed 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -112,7 +112,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 309389e20d..649aa915b6 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"
@@ -559,7 +560,7 @@ DefineIndex(Oid tableId,
IndexAmRoutine *amRoutine;
bool amcanorder;
bool amissummarizing;
- amoptions_function amoptions;
+ amreloptspecset_function amreloptspecsetfn;
bool partitioned;
bool safe_index;
Datum reloptions;
@@ -870,7 +871,7 @@ DefineIndex(Oid tableId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
amissummarizing = amRoutine->amsummarizing;
pfree(amRoutine);
@@ -885,10 +886,18 @@ DefineIndex(Oid tableId,
/*
* 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.
@@ -2195,8 +2204,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
Assert(attn < nkeycols);
opclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
else
opclassOptions[attn] = (Datum) 0;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7b6c69b7a5..e145bf7957 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"
@@ -699,7 +700,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;
@@ -842,19 +842,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);
- break;
+ {
+ /* 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)
@@ -4366,7 +4383,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);
@@ -4408,7 +4425,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.
@@ -4629,9 +4646,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8720,12 +8742,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));
@@ -14857,12 +14880,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 */
@@ -14882,38 +14906,56 @@ 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))));
+ 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),
@@ -14923,11 +14965,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;
@@ -15002,20 +15048,23 @@ 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.c
index 113b480731..01aace1f1a 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -333,10 +333,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
@@ -1052,11 +1051,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.c
index f4f35728b4..307195a148 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);
@@ -238,7 +238,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -275,7 +275,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -308,7 +308,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -516,7 +516,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 */
@@ -614,7 +614,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index d5c2b2ff0b..d0df5822bd 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1726,7 +1726,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;
@@ -1790,7 +1790,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index fa66b8017e..bafa48199b 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1156,6 +1156,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;
@@ -1179,15 +1180,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);
@@ -1296,9 +1298,12 @@ ProcessUtilitySlow(ParseState *pstate,
* lock on (for example) a relation on which we have no
* permissions.
*/
- lockmode = AlterTableGetLockLevel(atstmt->cmds);
- relid = AlterTableLookupRelation(atstmt, lockmode);
-
+ relid = AlterTableLookupRelation(atstmt, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index af978ccd4b..b295191d12 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"
@@ -149,7 +150,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index cc9b0c6524..761811d940 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -463,7 +463,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -478,11 +478,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;
@@ -493,7 +493,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index ec63cdc8e5..ea9552ca67 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,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/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index feee451d59..a611f9da2c 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -832,7 +832,7 @@ AppendStringCommandOption(PQExpBuffer buf, bool use_new_option_syntax,
{
AppendPlainCommandOption(buf, use_new_option_syntax, option_name);
- if (option_value != NULL)
+ if (option_value !=NULL)
{
size_t length = strlen(option_value);
char *escaped_value = palloc(1 + 2 * length);
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index f25c9d58a7..0ef48df437 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -140,10 +140,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,
@@ -190,6 +186,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.
*/
@@ -272,7 +271,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;
@@ -284,6 +282,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amreloptspecset_function amreloptspecset; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 1c786281dd..a552003224 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -37,6 +37,8 @@ typedef struct BrinStatsData
#define BRIN_DEFAULT_PAGES_PER_RANGE 128
+#define BRIN_MIN_PAGES_PER_RANGE 1
+#define BRIN_MAX_PAGES_PER_RANGE 131072
#define BrinGetPagesPerRange(relation) \
(AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
relation->rd_rel->relam == BRIN_AM_OID), \
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index a5a9772621..fd99dfae80 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"
/*
@@ -109,6 +110,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern void *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 3013a44bae..5ba3a7578b 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -109,6 +109,7 @@ extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
+extern void *gingetreloptspecset(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 7b8749c8db..4579f1b5ff 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
*/
@@ -479,7 +481,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
#define GIST_MIN_FILLFACTOR 10
#define GIST_DEFAULT_FILLFACTOR 90
-extern bytea *gistoptions(Datum reloptions, bool validate);
+extern void *gistgetreloptspecset(void);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 9c7d81525b..6fa680afe3 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -474,6 +473,7 @@ extern BlockNumber _hash_get_newblock_from_oldbucket(Relation rel, Bucket old_bu
extern Bucket _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
uint32 lowmask, uint32 maxbucket);
extern void _hash_kill_items(IndexScanDesc scan);
+extern void *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 7493043348..0ec72c7dd7 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1298,7 +1298,7 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
+extern void *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 0000000000..c87dc9e8d9
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,274 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2024, 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;
+
+/*
+ * Each Option Value item passes through certain life cycle. Option Value
+ * Status specifies at what point of life cycle this Option Value is now.
+ * Option Value Statuses are:
+ * EMPTY - Option Value structure have been just created
+ * RAW - Option Value have been read, but has not been parsed or validated yet.
+ * Use raw_value structure member to access Raw Value
+ * PARSED - Option Value have been parsed, you should use proper union member
+ * to access Parsed Option Value, according to Option Type
+ * FOR_RESET - Specifies that this Option is designated for resetting to
+ * default value
+ */
+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 Option
+ * Spec Set 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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 *namspace; /* 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 *namspace;
+ 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 *namspace,
+ 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_namspaces);
+extern List *optionsDefListFilterNamespaces(List *defList,
+ const char *namspace);
+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.h
index 81829b8270..f80e1e840c 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-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -22,178 +17,40 @@
#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 */
+ 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 *relopts, Size relopt_struct_size);
extern void register_reloptions_validator(local_relopts *relopts,
@@ -210,7 +67,7 @@ extern void add_local_real_reloption(local_relopts *relopts, 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 *relopts, const char *name,
@@ -219,29 +76,17 @@ extern void add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, char *validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index d6a4953120..7b113d7a76 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -189,9 +189,6 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
-/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
-
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index 2e9c757b30..a4d809cc29 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -529,6 +529,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page,
extern bool spgproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
+extern void *spggetreloptspecset(void);
/* spgdoinsert.c */
extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 85cbad3d0c..1c7bb7f925 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -36,7 +36,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index 18185d0206..a8744fc415 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"
@@ -23,11 +23,7 @@
PG_MODULE_MAGIC;
-/* parse table for fillRelOptions */
-relopt_parse_elt di_relopt_tab[6];
-
-/* Kind of relation options for dummy index */
-relopt_kind di_relopt_kind;
+void _PG_init(void);
typedef enum DummyAmEnum
{
@@ -47,7 +43,7 @@ typedef struct DummyIndexOptions
int option_string_null_offset;
} DummyIndexOptions;
-relopt_enum_elt_def dummyAmEnumValues[] =
+static opt_enum_elt_def dummyAmEnumValues[] =
{
{"one", DUMMY_AM_ENUM_ONE},
{"two", DUMMY_AM_ENUM_TWO},
@@ -61,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.
*/
@@ -216,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.
*/
@@ -308,7 +296,6 @@ dihandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = divacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = dicostestimate;
- amroutine->amoptions = dioptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = divalidate;
@@ -322,12 +309,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/modules/test_oat_hooks/expected/alter_table.out b/src/test/modules/test_oat_hooks/expected/alter_table.out
index 8cbacca2c9..8319cd0694 100644
--- a/src/test/modules/test_oat_hooks/expected/alter_table.out
+++ b/src/test/modules/test_oat_hooks/expected/alter_table.out
@@ -82,6 +82,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -89,6 +91,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -96,6 +100,8 @@ ALTER TABLE test_oat_schema.test_oat_tab FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -103,6 +109,8 @@ ALTER TABLE test_oat_schema.test_oat_tab NO FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -111,6 +119,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -120,6 +130,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -130,6 +142,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -139,6 +153,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
index 9de19b4e3f..67f0a828f9 100644
--- a/src/test/regress/expected/reloptions.out
+++ b/src/test/regress/expected/reloptions.out
@@ -175,7 +175,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
@@ -194,6 +194,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.sql
index 24fbe0b478..9b404295c0 100644
--- a/src/test/regress/sql/reloptions.sql
+++ b/src/test/regress/sql/reloptions.sql
@@ -112,6 +112,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
--
--
2.39.2
While fixing the patch according to jian he recommendations, I come to a
conclusion that using void* pointer to a structure is really bad idea. Do not
know why I ever did it that way from start.
So I fixed it.
Changes of this fix can be seen here:
https://gitlab.com/dhyannataraj/postgres/-/commit/be4d9e16
The whole patch is attached to this mail.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
v9-0001-New-options-engine.patchtext/x-patch; charset=unicode-2-0-utf-8; name=v9-0001-New-options-engine.patchDownload
From 85f8be68d718dc2df36bc2bb4987035233f7b148 Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Sat, 22 Jun 2024 21:09:37 +0300
Subject: [PATCH v9 1/1] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
doc/src/sgml/indexam.sgml | 25 +-
src/backend/access/brin/brin.c | 46 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/meson.build | 1 +
src/backend/access/common/options.c | 1366 ++++++++++
src/backend/access/common/reloptions.c | 2234 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 19 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/bin/pg_basebackup/streamutil.c | 2 +-
src/include/access/amapi.h | 8 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 1 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 274 ++
src/include/access/reloptions.h | 204 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 148 +-
.../test_oat_hooks/expected/alter_table.out | 16 +
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
48 files changed, 2607 insertions(+), 2332 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index fba3ba7771..c35bf13981 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"
@@ -206,7 +207,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern options_spec_set *blrelopt_specset(void);
+extern void blReloptionPostprocess(void *, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 6836129c90..670c8b67d8 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.
*/
@@ -137,7 +97,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;
@@ -156,6 +116,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* Convert signature length from # of bits to # to words, rounding up */
+ opts->bloomLength = (opts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
+}
+
+
/*
* Fill BloomState structure for particular index.
*/
@@ -470,24 +439,37 @@ BloomInitMetapage(Relation index, ForkNumber forknum)
UnlockReleaseBuffer(metaBuffer);
}
-/*
- * Parse reloptions for bloom index, producing a BloomOptions struct.
- */
-bytea *
-bloptions(Datum reloptions, bool validate)
+options_spec_set *
+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/dblink/dblink.c b/contrib/dblink/dblink.c
index 755293456f..e39c8cd49a 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1938,7 +1938,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 249d82d3a0..26740d3ef7 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -197,7 +197,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 630b304338..e6df6dd53a 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -71,7 +71,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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index 517f20bc37..71581fba34 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
index e3c1539a1e..31b7d940e7 100644
--- a/doc/src/sgml/indexam.sgml
+++ b/doc/src/sgml/indexam.sgml
@@ -480,27 +480,12 @@ amcostestimate (PlannerInfo *root,
<para>
<programlisting>
-bytea *
-amoptions (ArrayType *reloptions,
- bool validate);
+void *
+amreloptspecset(void);
</programlisting>
- Parse and validate the reloptions array for an index. This is called only
- when a non-null reloptions array exists for the index.
- <parameter>reloptions</parameter> is a <type>text</type> array containing entries of the
- form <replaceable>name</replaceable><literal>=</literal><replaceable>value</replaceable>.
- The function should construct a <type>bytea</type> value, which will be copied
- into the <structfield>rd_options</structfield> field of the index's relcache entry.
- The data contents of the <type>bytea</type> value are open for the access
- method to define; most of the standard access methods use struct
- <structname>StdRdOptions</structname>.
- When <parameter>validate</parameter> is true, the function should report a suitable
- error message if any of the options are unrecognized or have invalid
- values; when <parameter>validate</parameter> is false, invalid entries should be
- silently ignored. (<parameter>validate</parameter> is false when loading options
- already stored in <structname>pg_catalog</structname>; an invalid entry could only
- be found if the access method has changed its rules for options, and in
- that case ignoring obsolete entries is appropriate.)
- It is OK to return NULL if default behavior is wanted.
+ Returns pointer to Options Spec Set that defines options available for an
+ index relation. This Spec Set will be used for parsing and validating index
+ relation options.
</para>
<para>
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index d0f3848c95..5eb7e04458 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"
@@ -279,7 +278,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;
@@ -294,6 +292,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = bringetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1329,23 +1328,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.
@@ -3000,3 +2982,29 @@ brin_fill_empty_ranges(BrinBuildState *state,
blkno += state->bs_pagesPerRange;
}
}
+
+static options_spec_set *brin_relopt_specset = NULL;
+
+options_spec_set *
+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/Makefile
index e78de31265..014d1b1562 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/meson.build b/src/backend/access/common/meson.build
index 14090c728f..0cfd469910 100644
--- a/src/backend/access/common/meson.build
+++ b/src/backend/access/common/meson.build
@@ -6,6 +6,7 @@ backend_sources += files(
'detoast.c',
'heaptuple.c',
'indextuple.c',
+ 'options.c',
'printsimple.c',
'printtup.c',
'relation.c',
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 0000000000..8cc790365b
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1366 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2024, 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 term "specification" instead of "definition" because term
+ * "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 Value 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 values. 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 + offset.
+ */
+
+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 Options 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.
+ *
+ * bytea_size - 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 *namspace, 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 (namspace)
+ {
+ spec_set->namspace = palloc(strlen(namspace) + 1);
+ strcpy(spec_set->namspace, namspace);
+ }
+ else
+ spec_set->namspace = NULL;
+ if (num_items_expected > 0)
+ {
+ spec_set->num_allocated = num_items_expected;
+ spec_set->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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", (int) 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 object 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->assert_on_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->namspace = palloc(strlen(def->defnamespace) + 1);
+ strcpy(option_dst->namspace, def->defnamespace);
+ }
+ else
+ option_dst->namspace = 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_builtin(array, TEXTOID, &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 j;
+ 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 (j = 0; j < text_len; j = j + pg_mblen(text_str))
+ {
+ if (text_str[j] == '=')
+ {
+ name_len = j;
+ 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->namspace = 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_namspaces array. Items without namspace are also accepted
+ *
+ * Used while validation of syntax parser output. Error is thrown if unproper
+ * namespace is found.
+ */
+void
+optionsDefListValdateNamespaces(List *defList, char **allowed_namspaces)
+{
+ 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_namspaces[i])
+ {
+ if (strcmp(def->defnamespace,
+ allowed_namspaces[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 "namspace" namespace. If "namspace"
+ * is NULL, only namespaseless options are returned
+ */
+List *
+optionsDefListFilterNamespaces(List *defList, const char *namspace)
+{
+ ListCell *cell;
+ List *result = NIL;
+
+ foreach(cell, defList)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ if ((!namspace && !def->defnamespace) ||
+ (namspace && def->defnamespace &&
+ strcmp(namspace, 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_builtin(array, TEXTOID, &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 unparsed 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 values 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->namspace && (!spec_set->namspace ||
+ strcmp(spec_set->namspace, option->namspace) != 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter namespace \"%s\"",
+ option->namspace)));
+
+ 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->namspace)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s.%s\"",
+ option->namspace, 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", (int) 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",
+ (int) 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 conversion.
+ */
+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;
+ if (defList)
+ data = optionsValuesToBytea(new_values, spec_set);
+ else
+ data = NULL;
+
+ spec_set->postprocess_fun(data, true);
+ if (data) 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/reloptions.c b/src/backend/access/common/reloptions.c
index d6eb5d8559..7ec05df7b9 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-2024, 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"
@@ -34,6 +31,7 @@
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "storage/bufmgr.h"
/*
* Contents of pg_class.reloptions
@@ -91,389 +89,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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -487,17 +104,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -505,225 +113,9 @@ static 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_heap);
+void oid_postvalidate(option_value *value);
/*
* init_local_reloptions
@@ -733,9 +125,8 @@ add_reloption(relopt_gen *newoption)
void
init_local_reloptions(local_relopts *relopts, Size relopt_struct_size)
{
- relopts->options = NIL;
relopts->validators = NIL;
- relopts->relopt_struct_size = relopt_struct_size;
+ relopts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, true, 0);
}
/*
@@ -749,112 +140,6 @@ register_reloptions_validator(local_relopts *relopts, relopts_validator validato
relopts->validators = lappend(relopts->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
@@ -865,47 +150,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);
}
/*
@@ -919,47 +165,8 @@ add_local_int_reloption(local_relopts *relopts, const char *name,
const char *desc, int default_val, int min_val,
int max_val, int offset)
{
- relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
- name, desc, default_val,
- min_val, max_val, 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * init_real_reloption
- * Allocate and initialize a new real reloption
- */
-static relopt_real *
-init_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption;
-
- newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
- name, desc, lockmode);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- return newoption;
-}
-
-/*
- * add_real_reloption
- * Add a new float reloption
- */
-void
-add_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption = init_real_reloption(kinds, name, desc,
- default_val, min_val,
- max_val, lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
}
/*
@@ -969,142 +176,28 @@ add_real_reloption(bits32 kinds, const char *name, const char *desc,
* '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;
- }
+add_local_real_reloption(local_relopts *relopts, const char *name,
+ const char *desc, double default_val,
+ double min_val, double max_val, int offset)
+{
+ optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
- return newoption;
}
/*
- * add_string_reloption
- * Add a new string reloption
+ * add_local_enum_reloption
+ * Add a new local enum 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.
+ * 'offset' is offset of int-typed field.
*/
void
-add_string_reloption(bits32 kinds, const char *name, const char *desc,
- const char *default_val, validate_string_relopt validator,
- LOCKMODE lockmode)
+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)
{
- relopt_string *newoption = init_string_reloption(kinds, name, desc,
- default_val,
- validator, NULL,
- lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ members, default_val, detailmsg);
}
/*
@@ -1120,247 +213,8 @@ add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset)
{
- relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- default_val,
- validator, filler,
- 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * Transform a relation options list (list of DefElem) into the text array
- * format that is kept in pg_class.reloptions, including only those options
- * that are in the passed namespace. The output values do not include the
- * namespace.
- *
- * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
- * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
- * reloptions value (possibly NULL), and we replace or remove entries
- * as needed.
- *
- * If acceptOidsOff is true, then we allow oids = false, but throw error when
- * on. This is solely needed for backwards compatibility.
- *
- * Note that this is not responsible for determining whether the options
- * are valid, but it does check that namespaces for all the options given are
- * listed in validnsps. The NULL namespace is always valid and need not be
- * explicitly listed. Passing a NULL pointer means that only the NULL
- * namespace is valid.
- *
- * Both oldOptions and the result are text arrays (or NULL for "default"),
- * but we declare them as Datums to avoid including array.h in reloptions.h.
- */
-Datum
-transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
- char *validnsps[], bool acceptOidsOff, bool isReset)
-{
- Datum result;
- ArrayBuildState *astate;
- ListCell *cell;
-
- /* no change if empty list */
- if (defList == NIL)
- return oldOptions;
-
- /* We build new array using accumArrayResult */
- astate = NULL;
-
- /* Copy any oldOptions that aren't to be replaced */
- if (PointerIsValid(DatumGetPointer(oldOptions)))
- {
- ArrayType *array = DatumGetArrayTypeP(oldOptions);
- Datum *oldoptions;
- int noldoptions;
- int i;
-
- deconstruct_array_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
-/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
- */
-List *
-untransformRelOptions(Datum options)
-{
- List *result = NIL;
- ArrayType *array;
- Datum *optiondatums;
- int noptions;
- int i;
-
- /* Nothing to do if no options */
- if (!PointerIsValid(DatumGetPointer(options)))
- return result;
-
- array = DatumGetArrayTypeP(options);
-
- deconstruct_array_builtin(array, TEXTOID, &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(p);
- }
- result = lappend(result, makeDefElem(s, val, -1));
- }
-
- return result;
+ optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, offset,
+ NULL, default_val, validator, filler);
}
/*
@@ -1377,12 +231,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,
@@ -1396,544 +251,326 @@ 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_builtin(array, TEXTOID, &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.
+ * 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.
*
- * options is a reloption text array as constructed by transformRelOptions.
- * kind specifies the family of options to be processed.
+ * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
+ * since they are only used by the AV procs and don't change anything
+ * currently executing.
*
- * 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.
+ * Fillfactor can be set because it applies only to subsequent changes made to
+ * data blocks, as documented in heapio.c
*
- * 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).
+ * 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.
*
- * 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.
- */
-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++;
- }
- }
+ * 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.
+ */
+
+
+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)
+ 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);
-
- *numrelopts = numoptions;
- return reloptions;
-}
-
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
+ 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\"."));
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
+ 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 (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
+ 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);
- return values;
+ return stdrd_relopt_spec_set;
}
-/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
- */
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
-
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
- }
+static options_spec_set *heap_relopt_spec_set = NULL;
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
/*
- * 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).
+ * 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 void *
-allocateReloptStruct(Size base, relopt_value *options, int numoptions)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- Size size = base;
- int i;
-
- for (i = 0; i < numoptions; i++)
+ if (data)
{
- relopt_value *optval = &options[i];
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
-
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
-
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-
- 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)
+static options_spec_set *toast_relopt_spec_set = NULL;
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- int i;
- int offset = basesize;
+ if (toast_relopt_spec_set)
+ return toast_relopt_spec_set;
- for (i = 0; i < numoptions; i++)
- {
- int j;
- bool found = false;
+ toast_relopt_spec_set = get_stdrd_relopt_spec_set(false);
+ toast_relopt_spec_set->postprocess_fun = toast_options_postprocess;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+ return toast_relopt_spec_set;
}
/*
- * Option parser for anything that uses StdRdOptions.
+ * Do not allow to set any option on partitioned table
*/
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+static void
+partitioned_options_postprocess(void *data, bool validate)
{
- 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));
+ if (data && validate)
+ ereport(ERROR,
+ errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot specify storage parameters for a partitioned table"),
+ errhint("Specify storage parameters for its leaf partitions instead."));
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
+options_spec_set *
+get_partitioned_relopt_spec_set(void)
+{
+ if (partitioned_relopt_spec_set)
+ return partitioned_relopt_spec_set;
+ partitioned_relopt_spec_set = get_stdrd_relopt_spec_set(true);
- pfree(options);
+ /* No options for now, so Spec Set is empty */
+ partitioned_relopt_spec_set->postprocess_fun =
+ partitioned_options_postprocess;
- return rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1944,158 +581,165 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
+
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- foreach(lc, relopts->options)
+ if (validate && relopts->validators)
{
- local_relopt *opt = lfirst(lc);
+ int val_count = list_length(values);
+ int i;
+ option_value *val_array;
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ val_array = palloc(sizeof(option_value) * val_count);
- i++;
- }
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (validate)
foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
- if (elems)
- pfree(elems);
+ pfree(val_array);
+ }
return opts;
}
/*
- * Option parser for partitioned tables
- */
-bytea *
-partitioned_table_reloptions(Datum reloptions, bool validate)
-{
- if (validate && reloptions)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot specify storage parameters for a partitioned table"),
- errhint("Specify storage parameters for its leaf partitions instead."));
- return NULL;
-}
-
-/*
- * Option parser for views
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an Options Spec Set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an Options Spec Set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2105,33 +749,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.c
index 5747ae6a4c..6b637a81bf 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"
@@ -27,6 +27,7 @@
#include "utils/index_selfuncs.h"
#include "utils/rel.h"
#include "utils/typcache.h"
+#include "utils/guc.h"
/*
@@ -69,7 +70,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;
@@ -84,6 +84,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -598,21 +599,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
*
@@ -699,3 +685,29 @@ ginUpdateStats(Relation index, const GinStatsData *stats, bool is_build)
END_CRIT_SECTION();
}
+
+static options_spec_set *gin_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index ed4ffa63a7..b5a1521a83 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -91,7 +91,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;
@@ -106,6 +105,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.c
index 78e98d68b1..ed43b1bbda 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 "common/pg_prng.h"
#include "storage/indexfsm.h"
#include "utils/float.h"
@@ -907,20 +907,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.
*
@@ -1055,3 +1041,39 @@ gistGetFakeLSN(Relation rel)
return GetFakeLSNForUnloggedRel();
}
}
+
+/* values from GistOptBufferingMode */
+static 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;
+
+options_spec_set *
+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.c
index 01d06b7c32..aae9541762 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -89,7 +89,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;
@@ -104,6 +103,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.c
index 20028f5cd1..0d1f7208d4 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 "utils/lsyscache.h"
@@ -271,19 +271,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
*/
@@ -619,3 +606,22 @@ _hash_kill_items(IndexScanDesc scan)
else
_hash_relbuf(rel, buf);
}
+
+static options_spec_set *hash_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 686a3206f7..cbcd5b8976 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -21,6 +21,7 @@
#include "access/nbtree.h"
#include "access/relscan.h"
#include "access/xloginsert.h"
+#include "access/options.h"
#include "commands/progress.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
@@ -133,7 +134,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;
@@ -148,6 +148,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1445,3 +1446,35 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+static options_spec_set *bt_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 05f0d23dc0..65658a5023 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 "commands/progress.h"
#include "lib/qunique.h"
@@ -4557,23 +4557,6 @@ BTreeShmemInit(void)
Assert(found);
}
-bytea *
-btoptions(Datum reloptions, bool validate)
-{
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(BTOptions, fillfactor)},
- {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
- offsetof(BTOptions, vacuum_cleanup_index_scale_factor)},
- {"deduplicate_items", RELOPT_TYPE_BOOL,
- offsetof(BTOptions, deduplicate_items)}
- };
-
- return (bytea *) build_reloptions(reloptions, validate,
- RELOPT_KIND_BTREE,
- sizeof(BTOptions),
- tab, lengthof(tab));
-}
-
/*
* btproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 3f793125f7..7ab983b036 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"
@@ -76,7 +76,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;
@@ -91,6 +90,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -733,22 +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).
@@ -1348,3 +1332,24 @@ spgproperty(Oid index_oid, int attno,
return true;
}
+
+static options_spec_set *spgist_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 62050f4dc5..936f640fdf 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -85,6 +85,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);
@@ -119,14 +120,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.c
index cf61bbac1f..556cad13ed 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -112,7 +112,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 309389e20d..649aa915b6 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"
@@ -559,7 +560,7 @@ DefineIndex(Oid tableId,
IndexAmRoutine *amRoutine;
bool amcanorder;
bool amissummarizing;
- amoptions_function amoptions;
+ amreloptspecset_function amreloptspecsetfn;
bool partitioned;
bool safe_index;
Datum reloptions;
@@ -870,7 +871,7 @@ DefineIndex(Oid tableId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
amissummarizing = amRoutine->amsummarizing;
pfree(amRoutine);
@@ -885,10 +886,18 @@ DefineIndex(Oid tableId,
/*
* 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.
@@ -2195,8 +2204,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
Assert(attn < nkeycols);
opclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
else
opclassOptions[attn] = (Datum) 0;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7b6c69b7a5..e145bf7957 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"
@@ -699,7 +700,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;
@@ -842,19 +842,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);
- break;
+ {
+ /* 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)
@@ -4366,7 +4383,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);
@@ -4408,7 +4425,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.
@@ -4629,9 +4646,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8720,12 +8742,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));
@@ -14857,12 +14880,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 */
@@ -14882,38 +14906,56 @@ 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))));
+ 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),
@@ -14923,11 +14965,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;
@@ -15002,20 +15048,23 @@ 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.c
index 113b480731..01aace1f1a 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -333,10 +333,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
@@ -1052,11 +1051,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.c
index f4f35728b4..307195a148 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);
@@ -238,7 +238,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -275,7 +275,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -308,7 +308,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -516,7 +516,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 */
@@ -614,7 +614,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index d5c2b2ff0b..d0df5822bd 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1726,7 +1726,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;
@@ -1790,7 +1790,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index fa66b8017e..bafa48199b 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1156,6 +1156,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;
@@ -1179,15 +1180,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);
@@ -1296,9 +1298,12 @@ ProcessUtilitySlow(ParseState *pstate,
* lock on (for example) a relation on which we have no
* permissions.
*/
- lockmode = AlterTableGetLockLevel(atstmt->cmds);
- relid = AlterTableLookupRelation(atstmt, lockmode);
-
+ relid = AlterTableLookupRelation(atstmt, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index af978ccd4b..b295191d12 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"
@@ -149,7 +150,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index cc9b0c6524..761811d940 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -463,7 +463,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -478,11 +478,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;
@@ -493,7 +493,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index ec63cdc8e5..ea9552ca67 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,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/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index feee451d59..a611f9da2c 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -832,7 +832,7 @@ AppendStringCommandOption(PQExpBuffer buf, bool use_new_option_syntax,
{
AppendPlainCommandOption(buf, use_new_option_syntax, option_name);
- if (option_value != NULL)
+ if (option_value !=NULL)
{
size_t length = strlen(option_value);
char *escaped_value = palloc(1 + 2 * length);
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index f25c9d58a7..97ecede1d7 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -13,6 +13,7 @@
#define AMAPI_H
#include "access/genam.h"
+#include "access/options.h"
/*
* We don't wish to include planner header files here, since most of an index
@@ -140,9 +141,8 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
double *indexCorrelation,
double *indexPages);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
+/* get Spec Set for relation options */
+typedef options_spec_set *(*amreloptspecset_function) ();
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
@@ -272,7 +272,7 @@ typedef struct IndexAmRoutine
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
- amoptions_function amoptions;
+ amreloptspecset_function amreloptspecset; /* can be NULL */
amproperty_function amproperty; /* can be NULL */
ambuildphasename_function ambuildphasename; /* can be NULL */
amvalidate_function amvalidate;
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 1c786281dd..a552003224 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -37,6 +37,8 @@ typedef struct BrinStatsData
#define BRIN_DEFAULT_PAGES_PER_RANGE 128
+#define BRIN_MIN_PAGES_PER_RANGE 1
+#define BRIN_MAX_PAGES_PER_RANGE 131072
#define BrinGetPagesPerRange(relation) \
(AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
relation->rd_rel->relam == BRIN_AM_OID), \
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index a5a9772621..0ea5d1f7cc 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -109,6 +109,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern options_spec_set *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 3013a44bae..2532b29ae6 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -109,6 +109,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 options_spec_set *gingetreloptspecset(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 7b8749c8db..63ca6e1ae3 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
*/
@@ -479,7 +481,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
#define GIST_MIN_FILLFACTOR 10
#define GIST_DEFAULT_FILLFACTOR 90
-extern bytea *gistoptions(Datum reloptions, bool validate);
+extern options_spec_set *gistgetreloptspecset(void);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 9c7d81525b..39f88e889a 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -474,6 +473,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 options_spec_set *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 7493043348..72080f5351 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1298,7 +1298,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 options_spec_set *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 0000000000..c87dc9e8d9
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,274 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2024, 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;
+
+/*
+ * Each Option Value item passes through certain life cycle. Option Value
+ * Status specifies at what point of life cycle this Option Value is now.
+ * Option Value Statuses are:
+ * EMPTY - Option Value structure have been just created
+ * RAW - Option Value have been read, but has not been parsed or validated yet.
+ * Use raw_value structure member to access Raw Value
+ * PARSED - Option Value have been parsed, you should use proper union member
+ * to access Parsed Option Value, according to Option Type
+ * FOR_RESET - Specifies that this Option is designated for resetting to
+ * default value
+ */
+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 Option
+ * Spec Set 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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 *namspace; /* 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 *namspace;
+ 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 *namspace,
+ 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_namspaces);
+extern List *optionsDefListFilterNamespaces(List *defList,
+ const char *namspace);
+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.h
index 81829b8270..3572345102 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-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -25,175 +20,36 @@
#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 */
+ 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 *relopts, Size relopt_struct_size);
extern void register_reloptions_validator(local_relopts *relopts,
@@ -210,7 +66,7 @@ extern void add_local_real_reloption(local_relopts *relopts, 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 *relopts, const char *name,
@@ -219,29 +75,17 @@ extern void add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, char *validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index d6a4953120..7b113d7a76 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -189,9 +189,6 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
-/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
-
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index 2e9c757b30..7e26fb3e55 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 options_spec_set *spggetreloptspecset(void);
/* spgdoinsert.c */
extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 85cbad3d0c..1c7bb7f925 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -36,7 +36,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index 18185d0206..8d4150cd86 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"
@@ -23,11 +23,7 @@
PG_MODULE_MAGIC;
-/* parse table for fillRelOptions */
-relopt_parse_elt di_relopt_tab[6];
-
-/* Kind of relation options for dummy index */
-relopt_kind di_relopt_kind;
+void _PG_init(void);
typedef enum DummyAmEnum
{
@@ -47,7 +43,7 @@ typedef struct DummyIndexOptions
int option_string_null_offset;
} DummyIndexOptions;
-relopt_enum_elt_def dummyAmEnumValues[] =
+static opt_enum_elt_def dummyAmEnumValues[] =
{
{"one", DUMMY_AM_ENUM_ONE},
{"two", DUMMY_AM_ENUM_TWO},
@@ -61,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;
+options_spec_set *digetreloptspecset(void);
+
+options_spec_set *
+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.
*/
@@ -216,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.
*/
@@ -308,7 +296,6 @@ dihandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = divacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = dicostestimate;
- amroutine->amoptions = dioptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = divalidate;
@@ -322,12 +309,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/modules/test_oat_hooks/expected/alter_table.out b/src/test/modules/test_oat_hooks/expected/alter_table.out
index 8cbacca2c9..8319cd0694 100644
--- a/src/test/modules/test_oat_hooks/expected/alter_table.out
+++ b/src/test/modules/test_oat_hooks/expected/alter_table.out
@@ -82,6 +82,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -89,6 +91,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -96,6 +100,8 @@ ALTER TABLE test_oat_schema.test_oat_tab FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -103,6 +109,8 @@ ALTER TABLE test_oat_schema.test_oat_tab NO FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -111,6 +119,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -120,6 +130,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -130,6 +142,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -139,6 +153,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
index 9de19b4e3f..67f0a828f9 100644
--- a/src/test/regress/expected/reloptions.out
+++ b/src/test/regress/expected/reloptions.out
@@ -175,7 +175,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
@@ -194,6 +194,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.sql
index 24fbe0b478..9b404295c0 100644
--- a/src/test/regress/sql/reloptions.sql
+++ b/src/test/regress/sql/reloptions.sql
@@ -112,6 +112,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
--
--
2.39.2
I've rebased patch against current master, added new autovacuum option added
in 306dc520 and make changes according to "Turn 'validnsps' static variables
into locals" patch 1e35951e
Now I gess I will try to split the patch into several parts (two for a start).
I do not think it should be commit part by part, it will make no sense, but
this should simplify reading and understanding.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
v10-0001-New-options-engine.patchtext/x-patch; charset=unicode-2-0-utf-8; name=v10-0001-New-options-engine.patchDownload
From e6cafc6e9b4aa8c7adb902a70a0386394d4854a3 Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Mon, 10 Feb 2025 21:08:17 +0300
Subject: [PATCH v10 1/1] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
doc/src/sgml/indexam.sgml | 25 +-
src/backend/access/brin/brin.c | 46 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/meson.build | 1 +
src/backend/access/common/options.c | 1367 ++++++++++
src/backend/access/common/reloptions.c | 2259 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 20 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 1 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 274 ++
src/include/access/reloptions.h | 204 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 148 +-
.../test_oat_hooks/expected/alter_table.out | 16 +
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
47 files changed, 2623 insertions(+), 2342 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 648167045f4..47f697f66a8 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"
@@ -203,7 +204,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern options_spec_set *blrelopt_specset(void);
+extern void blReloptionPostprocess(void *, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 04b61042a57..b4f55daf49b 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 "commands/vacuum.h"
#include "storage/bufmgr.h"
@@ -31,52 +31,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->amcanreturn = NULL;
amroutine->amcostestimate = blcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = bloptions;
+ amroutine->amreloptspecset = blrelopt_specset;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = blvalidate;
@@ -156,6 +116,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* Convert signature length from # of bits to # to words, rounding up */
+ opts->bloomLength = (opts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
+}
+
+
/*
* Fill BloomState structure for particular index.
*/
@@ -470,24 +439,37 @@ BloomInitMetapage(Relation index, ForkNumber forknum)
UnlockReleaseBuffer(metaBuffer);
}
-/*
- * Parse reloptions for bloom index, producing a BloomOptions struct.
- */
-bytea *
-bloptions(Datum reloptions, bool validate)
+options_spec_set *
+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/dblink/dblink.c b/contrib/dblink/dblink.c
index bed2dee3d72..bdd4d83fdf0 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1938,7 +1938,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 678e754b2b9..043a3667638 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -201,7 +201,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index d0766f007d2..085a1228951 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -70,7 +70,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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index 08a7c56b5df..98956c5e22f 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
index d17fcbd5cec..af7004dd76c 100644
--- a/doc/src/sgml/indexam.sgml
+++ b/doc/src/sgml/indexam.sgml
@@ -500,27 +500,12 @@ amgettreeheight (Relation rel);
<para>
<programlisting>
-bytea *
-amoptions (ArrayType *reloptions,
- bool validate);
+void *
+amreloptspecset(void);
</programlisting>
- Parse and validate the reloptions array for an index. This is called only
- when a non-null reloptions array exists for the index.
- <parameter>reloptions</parameter> is a <type>text</type> array containing entries of the
- form <replaceable>name</replaceable><literal>=</literal><replaceable>value</replaceable>.
- The function should construct a <type>bytea</type> value, which will be copied
- into the <structfield>rd_options</structfield> field of the index's relcache entry.
- The data contents of the <type>bytea</type> value are open for the access
- method to define; most of the standard access methods use struct
- <structname>StdRdOptions</structname>.
- When <parameter>validate</parameter> is true, the function should report a suitable
- error message if any of the options are unrecognized or have invalid
- values; when <parameter>validate</parameter> is false, invalid entries should be
- silently ignored. (<parameter>validate</parameter> is false when loading options
- already stored in <structname>pg_catalog</structname>; an invalid entry could only
- be found if the access method has changed its rules for options, and in
- that case ignoring obsolete entries is appropriate.)
- It is OK to return NULL if default behavior is wanted.
+ Returns pointer to Options Spec Set that defines options available for an
+ index relation. This Spec Set will be used for parsing and validating index
+ relation options.
</para>
<para>
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index ccf824bbdb2..abcda91b7bd 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"
@@ -283,7 +282,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = brincostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = brinoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = brinvalidate;
@@ -300,6 +298,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = NULL;
amroutine->amtranslatecmptype = NULL;
+ amroutine->amreloptspecset = bringetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1334,23 +1333,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.
@@ -3009,3 +2991,29 @@ brin_fill_empty_ranges(BrinBuildState *state,
blkno += state->bs_pagesPerRange;
}
}
+
+static options_spec_set *brin_relopt_specset = NULL;
+
+options_spec_set *
+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/Makefile
index e78de312659..014d1b15622 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/meson.build b/src/backend/access/common/meson.build
index e3cdbe7a22e..9f9bbc494cc 100644
--- a/src/backend/access/common/meson.build
+++ b/src/backend/access/common/meson.build
@@ -6,6 +6,7 @@ backend_sources += files(
'detoast.c',
'heaptuple.c',
'indextuple.c',
+ 'options.c',
'printsimple.c',
'printtup.c',
'relation.c',
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 00000000000..fc42a02b573
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1367 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2025, 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 term "specification" instead of "definition" because term
+ * "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 Value 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 values. 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 + offset.
+ */
+
+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 Options 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.
+ *
+ * bytea_size - 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 *namspace, 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 (namspace)
+ {
+ spec_set->namspace = palloc(strlen(namspace) + 1);
+ strcpy(spec_set->namspace, namspace);
+ }
+ else
+ spec_set->namspace = NULL;
+ if (num_items_expected > 0)
+ {
+ spec_set->num_allocated = num_items_expected;
+ spec_set->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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", (int) 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 object 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->assert_on_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->namspace = palloc(strlen(def->defnamespace) + 1);
+ strcpy(option_dst->namspace, def->defnamespace);
+ }
+ else
+ option_dst->namspace = 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_builtin(array, TEXTOID, &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 j;
+ 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 (j = 0; j < text_len; j = j + pg_mblen(text_str))
+ {
+ if (text_str[j] == '=')
+ {
+ name_len = j;
+ 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->namspace = 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_namspaces array. Items without namspace are also accepted
+ *
+ * Used while validation of syntax parser output. Error is thrown if unproper
+ * namespace is found.
+ */
+void
+optionsDefListValdateNamespaces(List *defList,
+ const char * const* allowed_namspaces)
+{
+ 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_namspaces[i])
+ {
+ if (strcmp(def->defnamespace,
+ allowed_namspaces[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 "namspace" namespace. If "namspace"
+ * is NULL, only namespaseless options are returned
+ */
+List *
+optionsDefListFilterNamespaces(List *defList, const char *namspace)
+{
+ ListCell *cell;
+ List *result = NIL;
+
+ foreach(cell, defList)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ if ((!namspace && !def->defnamespace) ||
+ (namspace && def->defnamespace &&
+ strcmp(namspace, 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_builtin(array, TEXTOID, &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 unparsed 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 values 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->namspace && (!spec_set->namspace ||
+ strcmp(spec_set->namspace, option->namspace) != 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter namespace \"%s\"",
+ option->namspace)));
+
+ 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->namspace)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s.%s\"",
+ option->namspace, 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", (int) 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",
+ (int) 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 conversion.
+ */
+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;
+ if (defList)
+ data = optionsValuesToBytea(new_values, spec_set);
+ else
+ data = NULL;
+
+ spec_set->postprocess_fun(data, true);
+ if (data) 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/reloptions.c b/src/backend/access/common/reloptions.c
index 5731cf42f54..a5a78d092dc 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-2025, 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"
@@ -34,6 +31,7 @@
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "storage/bufmgr.h"
/*
* Contents of pg_class.reloptions
@@ -91,398 +89,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_max_threshold",
- "Maximum number of tuple updates or deletes prior to vacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -2, -1, 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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -496,17 +104,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -514,225 +113,9 @@ static 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_heap);
+void oid_postvalidate(option_value *value);
/*
* init_local_reloptions
@@ -742,9 +125,8 @@ add_reloption(relopt_gen *newoption)
void
init_local_reloptions(local_relopts *relopts, Size relopt_struct_size)
{
- relopts->options = NIL;
relopts->validators = NIL;
- relopts->relopt_struct_size = relopt_struct_size;
+ relopts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, true, 0);
}
/*
@@ -758,112 +140,6 @@ register_reloptions_validator(local_relopts *relopts, relopts_validator validato
relopts->validators = lappend(relopts->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
@@ -874,47 +150,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);
}
/*
@@ -928,47 +165,8 @@ add_local_int_reloption(local_relopts *relopts, const char *name,
const char *desc, int default_val, int min_val,
int max_val, int offset)
{
- relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
- name, desc, default_val,
- min_val, max_val, 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * init_real_reloption
- * Allocate and initialize a new real reloption
- */
-static relopt_real *
-init_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption;
-
- newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
- name, desc, lockmode);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- return newoption;
-}
-
-/*
- * add_real_reloption
- * Add a new float reloption
- */
-void
-add_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption = init_real_reloption(kinds, name, desc,
- default_val, min_val,
- max_val, lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
}
/*
@@ -978,142 +176,28 @@ add_real_reloption(bits32 kinds, const char *name, const char *desc,
* '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;
- }
+add_local_real_reloption(local_relopts *relopts, const char *name,
+ const char *desc, double default_val,
+ double min_val, double max_val, int offset)
+{
+ optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
- return newoption;
}
/*
- * add_string_reloption
- * Add a new string reloption
+ * add_local_enum_reloption
+ * Add a new local enum 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.
+ * 'offset' is offset of int-typed field.
*/
void
-add_string_reloption(bits32 kinds, const char *name, const char *desc,
- const char *default_val, validate_string_relopt validator,
- LOCKMODE lockmode)
+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)
{
- relopt_string *newoption = init_string_reloption(kinds, name, desc,
- default_val,
- validator, NULL,
- lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ members, default_val, detailmsg);
}
/*
@@ -1129,247 +213,8 @@ add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset)
{
- relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- default_val,
- validator, filler,
- 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * Transform a relation options list (list of DefElem) into the text array
- * format that is kept in pg_class.reloptions, including only those options
- * that are in the passed namespace. The output values do not include the
- * namespace.
- *
- * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
- * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
- * reloptions value (possibly NULL), and we replace or remove entries
- * as needed.
- *
- * If acceptOidsOff is true, then we allow oids = false, but throw error when
- * on. This is solely needed for backwards compatibility.
- *
- * Note that this is not responsible for determining whether the options
- * are valid, but it does check that namespaces for all the options given are
- * listed in validnsps. The NULL namespace is always valid and need not be
- * explicitly listed. Passing a NULL pointer means that only the NULL
- * namespace is valid.
- *
- * Both oldOptions and the result are text arrays (or NULL for "default"),
- * but we declare them as Datums to avoid including array.h in reloptions.h.
- */
-Datum
-transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
- const char *const 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_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
-/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
- */
-List *
-untransformRelOptions(Datum options)
-{
- List *result = NIL;
- ArrayType *array;
- Datum *optiondatums;
- int noptions;
- int i;
-
- /* Nothing to do if no options */
- if (!PointerIsValid(DatumGetPointer(options)))
- return result;
-
- array = DatumGetArrayTypeP(options);
-
- deconstruct_array_builtin(array, TEXTOID, &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(p);
- }
- result = lappend(result, makeDefElem(s, val, -1));
- }
-
- return result;
+ optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, offset,
+ NULL, default_val, validator, filler);
}
/*
@@ -1386,12 +231,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,
@@ -1405,546 +251,340 @@ 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_builtin(array, TEXTOID, &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.
+ * 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.
*
- * options is a reloption text array as constructed by transformRelOptions.
- * kind specifies the family of options to be processed.
+ * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
+ * since they are only used by the AV procs and don't change anything
+ * currently executing.
*
- * 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.
+ * Fillfactor can be set because it applies only to subsequent changes made to
+ * data blocks, as documented in heapio.c
*
- * 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).
+ * 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.
*
- * 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.
- */
-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++;
- }
- }
+ * 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.
+ */
+
+
+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)
+ 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);
+
+ optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_vacuum_max_threshold",
+ "Maximum number of tuple updates or deletes prior to vacuum",
+ ShareUpdateExclusiveLock,
+ offsetof(StdRdOptions, autovacuum) +
+ offsetof(AutoVacOpts, vacuum_max_threshold),
+ NULL, -2, -1, INT_MAX);
+
+ optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_vacuum_insert_threshold",
+ "Minimum number of tuple inserts prior to vacuum, or -1 to disable insert vacuums",
+ ShareUpdateExclusiveLock,
+ offsetof(StdRdOptions, autovacuum) +
+ offsetof(AutoVacOpts, vacuum_ins_threshold),
+ NULL, -2, -1, 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);
-
- *numrelopts = numoptions;
- return reloptions;
-}
-
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
+ 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\"."));
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
+ 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 (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
+ 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);
- return values;
+ return stdrd_relopt_spec_set;
}
-/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
- */
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
-
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
- }
+static options_spec_set *heap_relopt_spec_set = NULL;
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
/*
- * 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).
+ * 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 void *
-allocateReloptStruct(Size base, relopt_value *options, int numoptions)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- Size size = base;
- int i;
-
- for (i = 0; i < numoptions; i++)
+ if (data)
{
- relopt_value *optval = &options[i];
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
-
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
-
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-
- 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)
+static options_spec_set *toast_relopt_spec_set = NULL;
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- int i;
- int offset = basesize;
+ if (toast_relopt_spec_set)
+ return toast_relopt_spec_set;
- for (i = 0; i < numoptions; i++)
- {
- int j;
- bool found = false;
+ toast_relopt_spec_set = get_stdrd_relopt_spec_set(false);
+ toast_relopt_spec_set->postprocess_fun = toast_options_postprocess;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+ return toast_relopt_spec_set;
}
/*
- * Option parser for anything that uses StdRdOptions.
+ * Do not allow to set any option on partitioned table
*/
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+static void
+partitioned_options_postprocess(void *data, bool validate)
{
- 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_max_threshold", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_max_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));
+ if (data && validate)
+ ereport(ERROR,
+ errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot specify storage parameters for a partitioned table"),
+ errhint("Specify storage parameters for its leaf partitions instead."));
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
+options_spec_set *
+get_partitioned_relopt_spec_set(void)
+{
+ if (partitioned_relopt_spec_set)
+ return partitioned_relopt_spec_set;
+ partitioned_relopt_spec_set = get_stdrd_relopt_spec_set(true);
- pfree(options);
+ /* No options for now, so Spec Set is empty */
+ partitioned_relopt_spec_set->postprocess_fun =
+ partitioned_options_postprocess;
- return rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1955,158 +595,165 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
+
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- foreach(lc, relopts->options)
+ if (validate && relopts->validators)
{
- local_relopt *opt = lfirst(lc);
+ int val_count = list_length(values);
+ int i;
+ option_value *val_array;
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ val_array = palloc(sizeof(option_value) * val_count);
- i++;
- }
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (validate)
foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
- if (elems)
- pfree(elems);
+ pfree(val_array);
+ }
return opts;
}
/*
- * Option parser for partitioned tables
- */
-bytea *
-partitioned_table_reloptions(Datum reloptions, bool validate)
-{
- if (validate && reloptions)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot specify storage parameters for a partitioned table"),
- errhint("Specify storage parameters for its leaf partitions instead."));
- return NULL;
-}
-
-/*
- * Option parser for views
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an Options Spec Set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an Options Spec Set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2116,33 +763,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.c
index 2500d16b7bc..1526bee6bde 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"
@@ -27,6 +27,7 @@
#include "utils/index_selfuncs.h"
#include "utils/rel.h"
#include "utils/typcache.h"
+#include "utils/guc.h"
/*
@@ -70,7 +71,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = ginvalidate;
@@ -85,6 +85,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -599,21 +600,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
*
@@ -700,3 +686,29 @@ ginUpdateStats(Relation index, const GinStatsData *stats, bool is_build)
END_CRIT_SECTION();
}
+
+static options_spec_set *gin_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 4d858b65e1e..b017ae6fcdd 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -92,7 +92,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = gistcanreturn;
amroutine->amcostestimate = gistcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = gistoptions;
amroutine->amproperty = gistproperty;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = gistvalidate;
@@ -109,6 +108,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = NULL;
amroutine->amtranslatecmptype = gisttranslatecmptype;
+ amroutine->amreloptspecset = gistgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index dbc4ac639a2..bb3eea8b1dd 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 "common/pg_prng.h"
#include "storage/indexfsm.h"
#include "utils/float.h"
@@ -908,20 +908,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.
*
@@ -1109,3 +1095,39 @@ gisttranslatecmptype(CompareType cmptype, Oid opfamily, Oid opcintype)
result = OidFunctionCall1Coll(funcid, InvalidOid, Int32GetDatum(cmptype));
return DatumGetUInt16(result);
}
+
+/* values from GistOptBufferingMode */
+static 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;
+
+options_spec_set *
+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.c
index 63b568e7f24..33239680056 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -91,7 +91,6 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = hashcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = hashoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = hashvalidate;
@@ -108,6 +107,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = hashtranslatestrategy;
amroutine->amtranslatecmptype = hashtranslatecmptype;
+ amroutine->amreloptspecset = hashgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
index 66c39f60654..8cc1e079200 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 "utils/lsyscache.h"
@@ -271,19 +271,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
*/
@@ -619,3 +606,22 @@ _hash_kill_items(IndexScanDesc scan)
else
_hash_relbuf(rel, buf);
}
+
+static options_spec_set *hash_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 971405e89af..67ec1fda939 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -21,6 +21,7 @@
#include "access/nbtree.h"
#include "access/relscan.h"
#include "access/stratnum.h"
+#include "access/options.h"
#include "commands/progress.h"
#include "commands/vacuum.h"
#include "nodes/execnodes.h"
@@ -134,7 +135,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = btcanreturn;
amroutine->amcostestimate = btcostestimate;
amroutine->amgettreeheight = btgettreeheight;
- amroutine->amoptions = btoptions;
amroutine->amproperty = btproperty;
amroutine->ambuildphasename = btbuildphasename;
amroutine->amvalidate = btvalidate;
@@ -151,6 +151,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = btparallelrescan;
amroutine->amtranslatestrategy = bttranslatestrategy;
amroutine->amtranslatecmptype = bttranslatecmptype;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1551,3 +1552,35 @@ bttranslatecmptype(CompareType cmptype, Oid opfamily, Oid opcintype)
return InvalidStrategy;
}
}
+
+static options_spec_set *bt_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 693e43c674b..cf24295c5cd 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -18,7 +18,8 @@
#include <time.h>
#include "access/nbtree.h"
-#include "access/reloptions.h"
+#include "storage/lock.h"
+#include "access/relscan.h"
#include "commands/progress.h"
#include "miscadmin.h"
#include "utils/datum.h"
@@ -2715,23 +2716,6 @@ BTreeShmemInit(void)
Assert(found);
}
-bytea *
-btoptions(Datum reloptions, bool validate)
-{
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(BTOptions, fillfactor)},
- {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
- offsetof(BTOptions, vacuum_cleanup_index_scale_factor)},
- {"deduplicate_items", RELOPT_TYPE_BOOL,
- offsetof(BTOptions, deduplicate_items)}
- };
-
- return (bytea *) build_reloptions(reloptions, validate,
- RELOPT_KIND_BTREE,
- sizeof(BTOptions),
- tab, lengthof(tab));
-}
-
/*
* btproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 367c36ef9af..c203c174fb9 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"
@@ -77,7 +77,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = spgcanreturn;
amroutine->amcostestimate = spgcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = spgoptions;
amroutine->amproperty = spgproperty;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = spgvalidate;
@@ -94,6 +93,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = NULL;
amroutine->amtranslatecmptype = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -749,22 +749,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).
@@ -1363,3 +1347,24 @@ spgproperty(Oid index_oid, int attno,
return true;
}
+
+static options_spec_set *spgist_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 23cecd99c9e..a25b24af571 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -85,6 +85,7 @@ create_ctas_internal(List *attrList, IntoClause *into)
Datum toast_options;
const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
ObjectAddress intoRelationAddr;
+ List *toastDefList;
/* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
is_matview = (into->viewQuery != NULL);
@@ -119,14 +120,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.c
index c14e038d54f..1c6426e6c57 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -112,7 +112,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index f8d3ea820e1..9bfcffc39f2 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -20,6 +20,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"
@@ -570,8 +571,8 @@ DefineIndex(Oid tableId,
IndexAmRoutine *amRoutine;
bool amcanorder;
bool amissummarizing;
- amoptions_function amoptions;
bool exclusion;
+ amreloptspecset_function amreloptspecsetfn;
bool partitioned;
bool safe_index;
Datum reloptions;
@@ -893,7 +894,7 @@ DefineIndex(Oid tableId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
amissummarizing = amRoutine->amsummarizing;
pfree(amRoutine);
@@ -908,10 +909,18 @@ DefineIndex(Oid tableId,
/*
* 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.
@@ -2246,8 +2255,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
Assert(attn < nkeycols);
opclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
else
opclassOptions[attn] = (Datum) 0;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 5823fce9340..ea05e6307ed 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -21,6 +21,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"
@@ -741,7 +742,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
ListCell *listptr;
AttrNumber attnum;
bool partitioned;
- const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
LOCKMODE parentLockmode;
@@ -890,19 +890,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);
- break;
+ {
+ /* If it is not all listed above, then it if heap */
+ const char *const 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 */
+ const char *const 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)
@@ -4526,7 +4543,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);
@@ -4568,7 +4585,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.
@@ -4789,9 +4806,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8915,12 +8937,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));
@@ -15717,12 +15740,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];
- const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
+ List *optionsDefList;
+ options_spec_set *optionsSpecSet;
+ const char *const heap_namespaces[] = HEAP_RELOPT_NAMESPACES;
if (defList == NIL && operation != AT_ReplaceRelOptions)
return; /* nothing to do */
@@ -15742,38 +15766,56 @@ 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))));
+ 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),
@@ -15783,11 +15825,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;
@@ -15863,20 +15909,23 @@ 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.c
index 4ac2763c7f3..96fc47a1061 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -333,10 +333,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
@@ -1052,11 +1051,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.c
index f0835fc3070..87780f32309 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -80,7 +80,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
if (isnull)
fdw->options = NIL;
else
- fdw->options = untransformRelOptions(datum);
+ fdw->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -167,7 +167,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
if (isnull)
server->options = NIL;
else
- server->options = untransformRelOptions(datum);
+ server->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -239,7 +239,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -276,7 +276,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -309,7 +309,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -526,7 +526,7 @@ pg_options_to_table(PG_FUNCTION_ARGS)
List *options;
ReturnSetInfo *rsinfo;
- options = untransformRelOptions(array);
+ options = optionsTextArrayToDefList(array);
rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
/* prepare the result set */
@@ -624,7 +624,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index eb7716cd84c..9ff9b2b448e 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1923,7 +1923,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;
@@ -1987,7 +1987,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 25fe3d58016..56cc463ee0e 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1159,6 +1159,7 @@ ProcessUtilitySlow(ParseState *pstate,
CreateStmt *cstmt = (CreateStmt *) stmt;
Datum toast_options;
const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
+ List *toastDefList;
/* Remember transformed RangeVar for LIKE */
table_rv = cstmt->relation;
@@ -1182,15 +1183,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);
@@ -1299,9 +1301,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 5c8360c08b5..f2850d5fc38 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"
@@ -176,7 +177,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 398114373e9..6326c38d0df 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -465,7 +465,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -480,11 +480,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;
@@ -495,7 +495,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index 23458599298..3010692c442 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,8 @@ get_tablespace(Oid spcid)
opts = NULL;
else
{
- bytea *bytea_opts = tablespace_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_tablespace_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts));
memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 6723de75a4d..1b1330bf879 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -15,6 +15,7 @@
#include "access/cmptype.h"
#include "access/genam.h"
#include "access/stratnum.h"
+#include "access/options.h"
/*
* We don't wish to include planner header files here, since most of an index
@@ -159,9 +160,9 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
*/
typedef int (*amgettreeheight_function) (Relation rel);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
+
+/* get Spec Set for relation options */
+typedef options_spec_set *(*amreloptspecset_function) ();
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
@@ -292,7 +293,7 @@ typedef struct IndexAmRoutine
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
amgettreeheight_function amgettreeheight; /* can be NULL */
- amoptions_function amoptions;
+ amreloptspecset_function amreloptspecset; /* can be NULL */
amproperty_function amproperty; /* can be NULL */
ambuildphasename_function ambuildphasename; /* can be NULL */
amvalidate_function amvalidate;
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 821f1e02806..88434a55612 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -37,6 +37,8 @@ typedef struct BrinStatsData
#define BRIN_DEFAULT_PAGES_PER_RANGE 128
+#define BRIN_MIN_PAGES_PER_RANGE 1
+#define BRIN_MAX_PAGES_PER_RANGE 131072
#define BrinGetPagesPerRange(relation) \
(AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
relation->rd_rel->relam == BRIN_AM_OID), \
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index d093a0bf130..2513250fe6a 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -109,6 +109,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern options_spec_set *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index dcd1ae3fc34..3a15f747372 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -109,6 +109,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 options_spec_set *gingetreloptspecset(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 39404ec7cdb..7ed2230e0be 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
*/
@@ -479,7 +481,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
#define GIST_MIN_FILLFACTOR 10
#define GIST_DEFAULT_FILLFACTOR 90
-extern bytea *gistoptions(Datum reloptions, bool validate);
+extern options_spec_set *gistgetreloptspecset(void);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index e91f2b00ad9..e8cc60bb799 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -477,6 +476,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 options_spec_set *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 000c7289b80..271e1c82908 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1307,7 +1307,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 options_spec_set *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 00000000000..c1df04c90f4
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,274 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2025, 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;
+
+/*
+ * Each Option Value item passes through certain life cycle. Option Value
+ * Status specifies at what point of life cycle this Option Value is now.
+ * Option Value Statuses are:
+ * EMPTY - Option Value structure have been just created
+ * RAW - Option Value have been read, but has not been parsed or validated yet.
+ * Use raw_value structure member to access Raw Value
+ * PARSED - Option Value have been parsed, you should use proper union member
+ * to access Parsed Option Value, according to Option Type
+ * FOR_RESET - Specifies that this Option is designated for resetting to
+ * default value
+ */
+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 Option
+ * Spec Set 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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 *namspace; /* 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 *namspace;
+ 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 *namspace,
+ 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,
+ const char * const* allowed_namspaces);
+extern List *optionsDefListFilterNamespaces(List *defList,
+ const char *namspace);
+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.h
index 43445cdcc6c..b42bb614e03 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-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -25,175 +20,36 @@
#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 */
+ 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 *relopts, Size relopt_struct_size);
extern void register_reloptions_validator(local_relopts *relopts,
@@ -210,7 +66,7 @@ extern void add_local_real_reloption(local_relopts *relopts, 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 *relopts, const char *name,
@@ -219,29 +75,17 @@ extern void add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, const char *const validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index cbe9b347d8f..c92c8698b98 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -189,9 +189,6 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
-/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
-
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index cb43a278f46..9f78a726646 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -530,6 +530,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 options_spec_set *spggetreloptspecset(void);
/* spgdoinsert.c */
extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 6832470d387..03a656cb4a8 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -36,7 +36,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index 7586f8ae5e1..97467b73c92 100644
--- a/src/test/modules/dummy_index_am/dummy_index_am.c
+++ b/src/test/modules/dummy_index_am/dummy_index_am.c
@@ -14,18 +14,14 @@
#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"
PG_MODULE_MAGIC;
-/* parse table for fillRelOptions */
-static relopt_parse_elt di_relopt_tab[6];
-
-/* Kind of relation options for dummy index */
-static relopt_kind di_relopt_kind;
+void _PG_init(void);
typedef enum DummyAmEnum
{
@@ -45,7 +41,7 @@ typedef struct DummyIndexOptions
int option_string_null_offset;
} DummyIndexOptions;
-static relopt_enum_elt_def dummyAmEnumValues[] =
+static opt_enum_elt_def dummyAmEnumValues[] =
{
{"one", DUMMY_AM_ENUM_ONE},
{"two", DUMMY_AM_ENUM_TWO},
@@ -59,77 +55,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;
+options_spec_set *digetreloptspecset(void);
+
+options_spec_set *
+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.
*/
@@ -214,19 +215,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.
*/
@@ -307,7 +295,6 @@ dihandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = dicostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = dioptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = divalidate;
@@ -321,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/modules/test_oat_hooks/expected/alter_table.out b/src/test/modules/test_oat_hooks/expected/alter_table.out
index 8cbacca2c9b..8319cd06941 100644
--- a/src/test/modules/test_oat_hooks/expected/alter_table.out
+++ b/src/test/modules/test_oat_hooks/expected/alter_table.out
@@ -82,6 +82,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -89,6 +91,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -96,6 +100,8 @@ ALTER TABLE test_oat_schema.test_oat_tab FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -103,6 +109,8 @@ ALTER TABLE test_oat_schema.test_oat_tab NO FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -111,6 +119,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -120,6 +130,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -130,6 +142,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -139,6 +153,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
index 9de19b4e3f1..67f0a828f9f 100644
--- a/src/test/regress/expected/reloptions.out
+++ b/src/test/regress/expected/reloptions.out
@@ -175,7 +175,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
@@ -194,6 +194,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.sql
index 24fbe0b478d..9b404295c05 100644
--- a/src/test/regress/sql/reloptions.sql
+++ b/src/test/regress/sql/reloptions.sql
@@ -112,6 +112,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
--
--
2.39.2
В письме от понедельник, 10 февраля 2025 г. 21:35:40 MSK пользователь Nikolay
Shaplov написал:
And new rebase:
Now I added vacuum_max_eager_freeze_failure_rate option introduced in 052026c
commit.
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
v-11-0001-New-options-engine.patchtext/x-patch; charset=unicode-2-0-utf-8; name=v-11-0001-New-options-engine.patchDownload
From 26d0946110b439aa44ce82e128f613079b8b7f69 Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Wed, 26 Feb 2025 22:24:29 +0300
Subject: [PATCH v[11] 1/1] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
doc/src/sgml/indexam.sgml | 25 +-
src/backend/access/brin/brin.c | 46 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/meson.build | 1 +
src/backend/access/common/options.c | 1367 ++++++++++
src/backend/access/common/reloptions.c | 2278 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 20 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 1 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 274 ++
src/include/access/reloptions.h | 204 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 148 +-
.../test_oat_hooks/expected/alter_table.out | 16 +
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
47 files changed, 2630 insertions(+), 2354 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 648167045f4..47f697f66a8 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"
@@ -203,7 +204,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern options_spec_set *blrelopt_specset(void);
+extern void blReloptionPostprocess(void *, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 04b61042a57..b4f55daf49b 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 "commands/vacuum.h"
#include "storage/bufmgr.h"
@@ -31,52 +31,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->amcanreturn = NULL;
amroutine->amcostestimate = blcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = bloptions;
+ amroutine->amreloptspecset = blrelopt_specset;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = blvalidate;
@@ -156,6 +116,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* Convert signature length from # of bits to # to words, rounding up */
+ opts->bloomLength = (opts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
+}
+
+
/*
* Fill BloomState structure for particular index.
*/
@@ -470,24 +439,37 @@ BloomInitMetapage(Relation index, ForkNumber forknum)
UnlockReleaseBuffer(metaBuffer);
}
-/*
- * Parse reloptions for bloom index, producing a BloomOptions struct.
- */
-bytea *
-bloptions(Datum reloptions, bool validate)
+options_spec_set *
+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/dblink/dblink.c b/contrib/dblink/dblink.c
index bed2dee3d72..bdd4d83fdf0 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1938,7 +1938,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 0655bf532a0..95b8b1bd264 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -201,7 +201,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index d0766f007d2..085a1228951 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -70,7 +70,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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index 08a7c56b5df..98956c5e22f 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
index d17fcbd5cec..af7004dd76c 100644
--- a/doc/src/sgml/indexam.sgml
+++ b/doc/src/sgml/indexam.sgml
@@ -500,27 +500,12 @@ amgettreeheight (Relation rel);
<para>
<programlisting>
-bytea *
-amoptions (ArrayType *reloptions,
- bool validate);
+void *
+amreloptspecset(void);
</programlisting>
- Parse and validate the reloptions array for an index. This is called only
- when a non-null reloptions array exists for the index.
- <parameter>reloptions</parameter> is a <type>text</type> array containing entries of the
- form <replaceable>name</replaceable><literal>=</literal><replaceable>value</replaceable>.
- The function should construct a <type>bytea</type> value, which will be copied
- into the <structfield>rd_options</structfield> field of the index's relcache entry.
- The data contents of the <type>bytea</type> value are open for the access
- method to define; most of the standard access methods use struct
- <structname>StdRdOptions</structname>.
- When <parameter>validate</parameter> is true, the function should report a suitable
- error message if any of the options are unrecognized or have invalid
- values; when <parameter>validate</parameter> is false, invalid entries should be
- silently ignored. (<parameter>validate</parameter> is false when loading options
- already stored in <structname>pg_catalog</structname>; an invalid entry could only
- be found if the access method has changed its rules for options, and in
- that case ignoring obsolete entries is appropriate.)
- It is OK to return NULL if default behavior is wanted.
+ Returns pointer to Options Spec Set that defines options available for an
+ index relation. This Spec Set will be used for parsing and validating index
+ relation options.
</para>
<para>
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 60320440fc5..78358a97043 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"
@@ -283,7 +282,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = brincostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = brinoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = brinvalidate;
@@ -300,6 +298,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = NULL;
amroutine->amtranslatecmptype = NULL;
+ amroutine->amreloptspecset = bringetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1336,23 +1335,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.
@@ -3011,3 +2993,29 @@ brin_fill_empty_ranges(BrinBuildState *state,
blkno += state->bs_pagesPerRange;
}
}
+
+static options_spec_set *brin_relopt_specset = NULL;
+
+options_spec_set *
+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/Makefile
index e78de312659..014d1b15622 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/meson.build b/src/backend/access/common/meson.build
index e3cdbe7a22e..9f9bbc494cc 100644
--- a/src/backend/access/common/meson.build
+++ b/src/backend/access/common/meson.build
@@ -6,6 +6,7 @@ backend_sources += files(
'detoast.c',
'heaptuple.c',
'indextuple.c',
+ 'options.c',
'printsimple.c',
'printtup.c',
'relation.c',
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 00000000000..fc42a02b573
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1367 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2025, 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 term "specification" instead of "definition" because term
+ * "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 Value 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 values. 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 + offset.
+ */
+
+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 Options 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.
+ *
+ * bytea_size - 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 *namspace, 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 (namspace)
+ {
+ spec_set->namspace = palloc(strlen(namspace) + 1);
+ strcpy(spec_set->namspace, namspace);
+ }
+ else
+ spec_set->namspace = NULL;
+ if (num_items_expected > 0)
+ {
+ spec_set->num_allocated = num_items_expected;
+ spec_set->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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", (int) 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 object 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->assert_on_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->namspace = palloc(strlen(def->defnamespace) + 1);
+ strcpy(option_dst->namspace, def->defnamespace);
+ }
+ else
+ option_dst->namspace = 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_builtin(array, TEXTOID, &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 j;
+ 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 (j = 0; j < text_len; j = j + pg_mblen(text_str))
+ {
+ if (text_str[j] == '=')
+ {
+ name_len = j;
+ 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->namspace = 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_namspaces array. Items without namspace are also accepted
+ *
+ * Used while validation of syntax parser output. Error is thrown if unproper
+ * namespace is found.
+ */
+void
+optionsDefListValdateNamespaces(List *defList,
+ const char * const* allowed_namspaces)
+{
+ 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_namspaces[i])
+ {
+ if (strcmp(def->defnamespace,
+ allowed_namspaces[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 "namspace" namespace. If "namspace"
+ * is NULL, only namespaseless options are returned
+ */
+List *
+optionsDefListFilterNamespaces(List *defList, const char *namspace)
+{
+ ListCell *cell;
+ List *result = NIL;
+
+ foreach(cell, defList)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ if ((!namspace && !def->defnamespace) ||
+ (namspace && def->defnamespace &&
+ strcmp(namspace, 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_builtin(array, TEXTOID, &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 unparsed 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 values 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->namspace && (!spec_set->namspace ||
+ strcmp(spec_set->namspace, option->namspace) != 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter namespace \"%s\"",
+ option->namspace)));
+
+ 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->namspace)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s.%s\"",
+ option->namspace, 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", (int) 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",
+ (int) 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 conversion.
+ */
+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;
+ if (defList)
+ data = optionsValuesToBytea(new_values, spec_set);
+ else
+ data = NULL;
+
+ spec_set->postprocess_fun(data, true);
+ if (data) 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/reloptions.c b/src/backend/access/common/reloptions.c
index 59fb53e7707..45e8eaaee25 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-2025, 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"
@@ -34,6 +31,7 @@
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "storage/bufmgr.h"
/*
* Contents of pg_class.reloptions
@@ -91,408 +89,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_max_threshold",
- "Maximum number of tuple updates or deletes prior to vacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -2, -1, 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
- },
- {
- {
- "vacuum_max_eager_freeze_failure_rate",
- "Fraction of pages in a relation vacuum can scan and fail to freeze before disabling eager scanning.",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, 1.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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -506,17 +104,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -524,225 +113,9 @@ static 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_heap);
+void oid_postvalidate(option_value *value);
/*
* init_local_reloptions
@@ -752,9 +125,8 @@ add_reloption(relopt_gen *newoption)
void
init_local_reloptions(local_relopts *relopts, Size relopt_struct_size)
{
- relopts->options = NIL;
relopts->validators = NIL;
- relopts->relopt_struct_size = relopt_struct_size;
+ relopts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, true, 0);
}
/*
@@ -768,112 +140,6 @@ register_reloptions_validator(local_relopts *relopts, relopts_validator validato
relopts->validators = lappend(relopts->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
@@ -884,47 +150,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);
}
/*
@@ -938,47 +165,8 @@ add_local_int_reloption(local_relopts *relopts, const char *name,
const char *desc, int default_val, int min_val,
int max_val, int offset)
{
- relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
- name, desc, default_val,
- min_val, max_val, 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * init_real_reloption
- * Allocate and initialize a new real reloption
- */
-static relopt_real *
-init_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption;
-
- newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
- name, desc, lockmode);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- return newoption;
-}
-
-/*
- * add_real_reloption
- * Add a new float reloption
- */
-void
-add_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption = init_real_reloption(kinds, name, desc,
- default_val, min_val,
- max_val, lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
}
/*
@@ -987,143 +175,29 @@ add_real_reloption(bits32 kinds, const char *name, const char *desc,
*
* '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;
- }
+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)
+{
+ optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
- return newoption;
}
/*
- * add_string_reloption
- * Add a new string reloption
+ * add_local_enum_reloption
+ * Add a new local enum 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.
+ * 'offset' is offset of int-typed field.
*/
void
-add_string_reloption(bits32 kinds, const char *name, const char *desc,
- const char *default_val, validate_string_relopt validator,
- LOCKMODE lockmode)
+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)
{
- relopt_string *newoption = init_string_reloption(kinds, name, desc,
- default_val,
- validator, NULL,
- lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ members, default_val, detailmsg);
}
/*
@@ -1139,247 +213,8 @@ add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset)
{
- relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- default_val,
- validator, filler,
- 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * Transform a relation options list (list of DefElem) into the text array
- * format that is kept in pg_class.reloptions, including only those options
- * that are in the passed namespace. The output values do not include the
- * namespace.
- *
- * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
- * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
- * reloptions value (possibly NULL), and we replace or remove entries
- * as needed.
- *
- * If acceptOidsOff is true, then we allow oids = false, but throw error when
- * on. This is solely needed for backwards compatibility.
- *
- * Note that this is not responsible for determining whether the options
- * are valid, but it does check that namespaces for all the options given are
- * listed in validnsps. The NULL namespace is always valid and need not be
- * explicitly listed. Passing a NULL pointer means that only the NULL
- * namespace is valid.
- *
- * Both oldOptions and the result are text arrays (or NULL for "default"),
- * but we declare them as Datums to avoid including array.h in reloptions.h.
- */
-Datum
-transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
- const char *const 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_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
-/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
- */
-List *
-untransformRelOptions(Datum options)
-{
- List *result = NIL;
- ArrayType *array;
- Datum *optiondatums;
- int noptions;
- int i;
-
- /* Nothing to do if no options */
- if (!PointerIsValid(DatumGetPointer(options)))
- return result;
-
- array = DatumGetArrayTypeP(options);
-
- deconstruct_array_builtin(array, TEXTOID, &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(p);
- }
- result = lappend(result, makeDefElem(s, val, -1));
- }
-
- return result;
+ optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, offset,
+ NULL, default_val, validator, filler);
}
/*
@@ -1396,12 +231,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,
@@ -1415,548 +251,347 @@ 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_builtin(array, TEXTOID, &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.
+ * 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.
*
- * options is a reloption text array as constructed by transformRelOptions.
- * kind specifies the family of options to be processed.
+ * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
+ * since they are only used by the AV procs and don't change anything
+ * currently executing.
*
- * 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.
+ * Fillfactor can be set because it applies only to subsequent changes made to
+ * data blocks, as documented in heapio.c
*
- * 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).
+ * 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.
*
- * 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.
- */
-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++;
- }
- }
+ * 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.
+ */
+
+
+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)
+ 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);
+
+ optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_vacuum_max_threshold",
+ "Maximum number of tuple updates or deletes prior to vacuum",
+ ShareUpdateExclusiveLock,
+ offsetof(StdRdOptions, autovacuum) +
+ offsetof(AutoVacOpts, vacuum_max_threshold),
+ NULL, -2, -1, INT_MAX);
+
+ optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_vacuum_insert_threshold",
+ "Minimum number of tuple inserts prior to vacuum, or -1 to disable insert vacuums",
+ ShareUpdateExclusiveLock,
+ offsetof(StdRdOptions, autovacuum) +
+ offsetof(AutoVacOpts, vacuum_ins_threshold),
+ NULL, -2, -1, 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\"."));
- *numrelopts = numoptions;
- return reloptions;
-}
-
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
+ 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);
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
+ optionsSpecSetAddReal(stdrd_relopt_spec_set, "vacuum_max_eager_freeze_failure_rate",
+ "Fraction of pages in a relation vacuum can scan and fail to freeze before disabling eager scanning",
+ ShareUpdateExclusiveLock,
+ offsetof(StdRdOptions,
+ vacuum_max_eager_freeze_failure_rate),
+ NULL, -1, 0.0, 1.0);
- if (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
+ 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);
- return values;
+ return stdrd_relopt_spec_set;
}
-/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
- */
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
-
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
- }
+static options_spec_set *heap_relopt_spec_set = NULL;
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
/*
- * 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).
+ * 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 void *
-allocateReloptStruct(Size base, relopt_value *options, int numoptions)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- Size size = base;
- int i;
-
- for (i = 0; i < numoptions; i++)
+ if (data)
{
- relopt_value *optval = &options[i];
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
-
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
-
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-
- 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)
+static options_spec_set *toast_relopt_spec_set = NULL;
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- int i;
- int offset = basesize;
+ if (toast_relopt_spec_set)
+ return toast_relopt_spec_set;
- for (i = 0; i < numoptions; i++)
- {
- int j;
- bool found = false;
+ toast_relopt_spec_set = get_stdrd_relopt_spec_set(false);
+ toast_relopt_spec_set->postprocess_fun = toast_options_postprocess;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+ return toast_relopt_spec_set;
}
/*
- * Option parser for anything that uses StdRdOptions.
+ * Do not allow to set any option on partitioned table
*/
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+static void
+partitioned_options_postprocess(void *data, bool validate)
{
- 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_max_threshold", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_max_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)},
- {"vacuum_max_eager_freeze_failure_rate", RELOPT_TYPE_REAL,
- offsetof(StdRdOptions, vacuum_max_eager_freeze_failure_rate)}
- };
-
- return (bytea *) build_reloptions(reloptions, validate, kind,
- sizeof(StdRdOptions),
- tab, lengthof(tab));
+ if (data && validate)
+ ereport(ERROR,
+ errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot specify storage parameters for a partitioned table"),
+ errhint("Specify storage parameters for its leaf partitions instead."));
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
+options_spec_set *
+get_partitioned_relopt_spec_set(void)
+{
+ if (partitioned_relopt_spec_set)
+ return partitioned_relopt_spec_set;
+ partitioned_relopt_spec_set = get_stdrd_relopt_spec_set(true);
- pfree(options);
+ /* No options for now, so Spec Set is empty */
+ partitioned_relopt_spec_set->postprocess_fun =
+ partitioned_options_postprocess;
- return rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1967,158 +602,165 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
+
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- foreach(lc, relopts->options)
+ if (validate && relopts->validators)
{
- local_relopt *opt = lfirst(lc);
+ int val_count = list_length(values);
+ int i;
+ option_value *val_array;
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ val_array = palloc(sizeof(option_value) * val_count);
- i++;
- }
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (validate)
foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
- if (elems)
- pfree(elems);
+ pfree(val_array);
+ }
return opts;
}
/*
- * Option parser for partitioned tables
- */
-bytea *
-partitioned_table_reloptions(Datum reloptions, bool validate)
-{
- if (validate && reloptions)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot specify storage parameters for a partitioned table"),
- errhint("Specify storage parameters for its leaf partitions instead."));
- return NULL;
-}
-
-/*
- * Option parser for views
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an Options Spec Set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an Options Spec Set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2128,33 +770,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.c
index 1f9e58c4f1f..c27fed3cac5 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"
@@ -27,6 +27,7 @@
#include "utils/index_selfuncs.h"
#include "utils/rel.h"
#include "utils/typcache.h"
+#include "utils/guc.h"
/*
@@ -70,7 +71,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = ginvalidate;
@@ -85,6 +85,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -599,21 +600,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
*
@@ -700,3 +686,29 @@ ginUpdateStats(Relation index, const GinStatsData *stats, bool is_build)
END_CRIT_SECTION();
}
+
+static options_spec_set *gin_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 4d858b65e1e..b017ae6fcdd 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -92,7 +92,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = gistcanreturn;
amroutine->amcostestimate = gistcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = gistoptions;
amroutine->amproperty = gistproperty;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = gistvalidate;
@@ -109,6 +108,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = NULL;
amroutine->amtranslatecmptype = gisttranslatecmptype;
+ amroutine->amreloptspecset = gistgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index a6b701943d3..b578031207c 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 "common/pg_prng.h"
#include "storage/indexfsm.h"
#include "utils/float.h"
@@ -908,20 +908,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.
*
@@ -1109,3 +1095,39 @@ gisttranslatecmptype(CompareType cmptype, Oid opfamily)
result = OidFunctionCall1Coll(funcid, InvalidOid, Int32GetDatum(cmptype));
return DatumGetUInt16(result);
}
+
+/* values from GistOptBufferingMode */
+static 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;
+
+options_spec_set *
+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.c
index b24aae415ea..475c4f3fcee 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -91,7 +91,6 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = hashcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = hashoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = hashvalidate;
@@ -108,6 +107,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = hashtranslatestrategy;
amroutine->amtranslatecmptype = hashtranslatecmptype;
+ amroutine->amreloptspecset = hashgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
index 66c39f60654..8cc1e079200 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 "utils/lsyscache.h"
@@ -271,19 +271,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
*/
@@ -619,3 +606,22 @@ _hash_kill_items(IndexScanDesc scan)
else
_hash_relbuf(rel, buf);
}
+
+static options_spec_set *hash_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 411d5ac0b5f..69a84a298e0 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -21,6 +21,7 @@
#include "access/nbtree.h"
#include "access/relscan.h"
#include "access/stratnum.h"
+#include "access/options.h"
#include "commands/progress.h"
#include "commands/vacuum.h"
#include "nodes/execnodes.h"
@@ -134,7 +135,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = btcanreturn;
amroutine->amcostestimate = btcostestimate;
amroutine->amgettreeheight = btgettreeheight;
- amroutine->amoptions = btoptions;
amroutine->amproperty = btproperty;
amroutine->ambuildphasename = btbuildphasename;
amroutine->amvalidate = btvalidate;
@@ -151,6 +151,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = btparallelrescan;
amroutine->amtranslatestrategy = bttranslatestrategy;
amroutine->amtranslatecmptype = bttranslatecmptype;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1551,3 +1552,35 @@ bttranslatecmptype(CompareType cmptype, Oid opfamily)
return InvalidStrategy;
}
}
+
+static options_spec_set *bt_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 693e43c674b..cf24295c5cd 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -18,7 +18,8 @@
#include <time.h>
#include "access/nbtree.h"
-#include "access/reloptions.h"
+#include "storage/lock.h"
+#include "access/relscan.h"
#include "commands/progress.h"
#include "miscadmin.h"
#include "utils/datum.h"
@@ -2715,23 +2716,6 @@ BTreeShmemInit(void)
Assert(found);
}
-bytea *
-btoptions(Datum reloptions, bool validate)
-{
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(BTOptions, fillfactor)},
- {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
- offsetof(BTOptions, vacuum_cleanup_index_scale_factor)},
- {"deduplicate_items", RELOPT_TYPE_BOOL,
- offsetof(BTOptions, deduplicate_items)}
- };
-
- return (bytea *) build_reloptions(reloptions, validate,
- RELOPT_KIND_BTREE,
- sizeof(BTOptions),
- tab, lengthof(tab));
-}
-
/*
* btproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 367c36ef9af..c203c174fb9 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"
@@ -77,7 +77,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = spgcanreturn;
amroutine->amcostestimate = spgcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = spgoptions;
amroutine->amproperty = spgproperty;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = spgvalidate;
@@ -94,6 +93,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = NULL;
amroutine->amtranslatecmptype = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -749,22 +749,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).
@@ -1363,3 +1347,24 @@ spgproperty(Oid index_oid, int attno,
return true;
}
+
+static options_spec_set *spgist_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 44b4665ccd3..d59285b074e 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -85,6 +85,7 @@ create_ctas_internal(List *attrList, IntoClause *into)
Datum toast_options;
const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
ObjectAddress intoRelationAddr;
+ List *toastDefList;
/* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
is_matview = (into->viewQuery != NULL);
@@ -119,14 +120,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.c
index c14e038d54f..1c6426e6c57 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -112,7 +112,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index c92f5620ec1..b55b1cc0180 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -20,6 +20,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"
@@ -570,8 +571,8 @@ DefineIndex(Oid tableId,
IndexAmRoutine *amRoutine;
bool amcanorder;
bool amissummarizing;
- amoptions_function amoptions;
bool exclusion;
+ amreloptspecset_function amreloptspecsetfn;
bool partitioned;
bool safe_index;
Datum reloptions;
@@ -893,7 +894,7 @@ DefineIndex(Oid tableId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
amissummarizing = amRoutine->amsummarizing;
pfree(amRoutine);
@@ -908,10 +909,18 @@ DefineIndex(Oid tableId,
/*
* 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.
@@ -2246,8 +2255,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
Assert(attn < nkeycols);
opclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
else
opclassOptions[attn] = (Datum) 0;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index ce7d115667e..ae8f8961b7d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -21,6 +21,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"
@@ -738,7 +739,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
ListCell *listptr;
AttrNumber attnum;
bool partitioned;
- const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
LOCKMODE parentLockmode;
@@ -887,19 +887,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);
- break;
+ {
+ /* If it is not all listed above, then it if heap */
+ const char *const 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 */
+ const char *const 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)
@@ -4523,7 +4540,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);
@@ -4565,7 +4582,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.
@@ -4786,9 +4803,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8916,12 +8938,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));
@@ -15702,12 +15725,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];
- const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
+ List *optionsDefList;
+ options_spec_set *optionsSpecSet;
+ const char *const heap_namespaces[] = HEAP_RELOPT_NAMESPACES;
if (defList == NIL && operation != AT_ReplaceRelOptions)
return; /* nothing to do */
@@ -15727,38 +15751,56 @@ 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))));
+ 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),
@@ -15768,11 +15810,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;
@@ -15848,20 +15894,23 @@ 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.c
index a9005cc7212..5dc733905be 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -333,10 +333,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
@@ -1052,11 +1051,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.c
index f0835fc3070..87780f32309 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -80,7 +80,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
if (isnull)
fdw->options = NIL;
else
- fdw->options = untransformRelOptions(datum);
+ fdw->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -167,7 +167,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
if (isnull)
server->options = NIL;
else
- server->options = untransformRelOptions(datum);
+ server->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -239,7 +239,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -276,7 +276,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -309,7 +309,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -526,7 +526,7 @@ pg_options_to_table(PG_FUNCTION_ARGS)
List *options;
ReturnSetInfo *rsinfo;
- options = untransformRelOptions(array);
+ options = optionsTextArrayToDefList(array);
rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
/* prepare the result set */
@@ -624,7 +624,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index abbe1bb45a3..51c3fffe3e8 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1925,7 +1925,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;
@@ -1989,7 +1989,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 25fe3d58016..56cc463ee0e 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1159,6 +1159,7 @@ ProcessUtilitySlow(ParseState *pstate,
CreateStmt *cstmt = (CreateStmt *) stmt;
Datum toast_options;
const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
+ List *toastDefList;
/* Remember transformed RangeVar for LIKE */
table_rv = cstmt->relation;
@@ -1182,15 +1183,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);
@@ -1299,9 +1301,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 5c8360c08b5..f2850d5fc38 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"
@@ -176,7 +177,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 398114373e9..6326c38d0df 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -465,7 +465,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -480,11 +480,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;
@@ -495,7 +495,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index 23458599298..3010692c442 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,8 @@ get_tablespace(Oid spcid)
opts = NULL;
else
{
- bytea *bytea_opts = tablespace_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_tablespace_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts));
memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index fbe6b225ec9..c1a10ddf2d8 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -15,6 +15,7 @@
#include "access/cmptype.h"
#include "access/genam.h"
#include "access/stratnum.h"
+#include "access/options.h"
/*
* We don't wish to include planner header files here, since most of an index
@@ -159,9 +160,9 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
*/
typedef int (*amgettreeheight_function) (Relation rel);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
+
+/* get Spec Set for relation options */
+typedef options_spec_set *(*amreloptspecset_function) ();
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
@@ -292,7 +293,7 @@ typedef struct IndexAmRoutine
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
amgettreeheight_function amgettreeheight; /* can be NULL */
- amoptions_function amoptions;
+ amreloptspecset_function amreloptspecset; /* can be NULL */
amproperty_function amproperty; /* can be NULL */
ambuildphasename_function ambuildphasename; /* can be NULL */
amvalidate_function amvalidate;
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 821f1e02806..88434a55612 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -37,6 +37,8 @@ typedef struct BrinStatsData
#define BRIN_DEFAULT_PAGES_PER_RANGE 128
+#define BRIN_MIN_PAGES_PER_RANGE 1
+#define BRIN_MAX_PAGES_PER_RANGE 131072
#define BrinGetPagesPerRange(relation) \
(AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
relation->rd_rel->relam == BRIN_AM_OID), \
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index d093a0bf130..2513250fe6a 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -109,6 +109,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern options_spec_set *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 50478db9820..271482e427a 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -109,6 +109,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 options_spec_set *gingetreloptspecset(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 39404ec7cdb..7ed2230e0be 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
*/
@@ -479,7 +481,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
#define GIST_MIN_FILLFACTOR 10
#define GIST_DEFAULT_FILLFACTOR 90
-extern bytea *gistoptions(Datum reloptions, bool validate);
+extern options_spec_set *gistgetreloptspecset(void);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 073ad29b19b..3dccf6ed279 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -477,6 +476,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 options_spec_set *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index e4fdeca3402..d86f150e50e 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1307,7 +1307,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 options_spec_set *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 00000000000..c1df04c90f4
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,274 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2025, 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;
+
+/*
+ * Each Option Value item passes through certain life cycle. Option Value
+ * Status specifies at what point of life cycle this Option Value is now.
+ * Option Value Statuses are:
+ * EMPTY - Option Value structure have been just created
+ * RAW - Option Value have been read, but has not been parsed or validated yet.
+ * Use raw_value structure member to access Raw Value
+ * PARSED - Option Value have been parsed, you should use proper union member
+ * to access Parsed Option Value, according to Option Type
+ * FOR_RESET - Specifies that this Option is designated for resetting to
+ * default value
+ */
+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 Option
+ * Spec Set 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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 *namspace; /* 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 *namspace;
+ 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 *namspace,
+ 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,
+ const char * const* allowed_namspaces);
+extern List *optionsDefListFilterNamespaces(List *defList,
+ const char *namspace);
+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.h
index 43445cdcc6c..b42bb614e03 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-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -25,175 +20,36 @@
#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 */
+ 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 *relopts, Size relopt_struct_size);
extern void register_reloptions_validator(local_relopts *relopts,
@@ -210,7 +66,7 @@ extern void add_local_real_reloption(local_relopts *relopts, 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 *relopts, const char *name,
@@ -219,29 +75,17 @@ extern void add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, const char *const validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index cbe9b347d8f..c92c8698b98 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -189,9 +189,6 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
-/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
-
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index cb43a278f46..9f78a726646 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -530,6 +530,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 options_spec_set *spggetreloptspecset(void);
/* spgdoinsert.c */
extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 6832470d387..03a656cb4a8 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -36,7 +36,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index 7586f8ae5e1..97467b73c92 100644
--- a/src/test/modules/dummy_index_am/dummy_index_am.c
+++ b/src/test/modules/dummy_index_am/dummy_index_am.c
@@ -14,18 +14,14 @@
#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"
PG_MODULE_MAGIC;
-/* parse table for fillRelOptions */
-static relopt_parse_elt di_relopt_tab[6];
-
-/* Kind of relation options for dummy index */
-static relopt_kind di_relopt_kind;
+void _PG_init(void);
typedef enum DummyAmEnum
{
@@ -45,7 +41,7 @@ typedef struct DummyIndexOptions
int option_string_null_offset;
} DummyIndexOptions;
-static relopt_enum_elt_def dummyAmEnumValues[] =
+static opt_enum_elt_def dummyAmEnumValues[] =
{
{"one", DUMMY_AM_ENUM_ONE},
{"two", DUMMY_AM_ENUM_TWO},
@@ -59,77 +55,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;
+options_spec_set *digetreloptspecset(void);
+
+options_spec_set *
+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.
*/
@@ -214,19 +215,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.
*/
@@ -307,7 +295,6 @@ dihandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = dicostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = dioptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = divalidate;
@@ -321,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/modules/test_oat_hooks/expected/alter_table.out b/src/test/modules/test_oat_hooks/expected/alter_table.out
index 8cbacca2c9b..8319cd06941 100644
--- a/src/test/modules/test_oat_hooks/expected/alter_table.out
+++ b/src/test/modules/test_oat_hooks/expected/alter_table.out
@@ -82,6 +82,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -89,6 +91,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -96,6 +100,8 @@ ALTER TABLE test_oat_schema.test_oat_tab FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -103,6 +109,8 @@ ALTER TABLE test_oat_schema.test_oat_tab NO FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -111,6 +119,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -120,6 +130,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -130,6 +142,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -139,6 +153,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
index 9de19b4e3f1..67f0a828f9f 100644
--- a/src/test/regress/expected/reloptions.out
+++ b/src/test/regress/expected/reloptions.out
@@ -175,7 +175,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
@@ -194,6 +194,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.sql
index 24fbe0b478d..9b404295c05 100644
--- a/src/test/regress/sql/reloptions.sql
+++ b/src/test/regress/sql/reloptions.sql
@@ -112,6 +112,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
--
--
2.39.2
В письме от среда, 26 февраля 2025 г. 22:33:58 MSK пользователь Nikolay
Shaplov написал:
And new rebase. Conflict with 8492feb9 commit needed manual resolving.
And since I am writing anyway, will say couple of words about the patch.
Some colleagues of mine says, that they would be happy to have ability to add
custom namespaces to heap options. They write extensions that adds this or
that feature to the storage, and this feature might need some custom
configuring. And it would be nice to configure it via heap-options. We already
have namespaces in reloptions, we can have more and make them custom.
This patch does not provide custom namespaces to reloptions, but it replaces
current reloption engine with a new one. And this new engine is very flexible,
you can easily add custom namespaces using it.
So review this patch now, and you will have custom option namespaces in
future (I think I will write it too) :-)
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
v-12-0001-New-options-engine.patchtext/x-patch; charset=unicode-2-0-utf-8; name=v-12-0001-New-options-engine.patchDownload
From 2c21270fd0b9bdfad8cdd57d9b43f40bb0e2ded4 Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Wed, 26 Feb 2025 22:24:29 +0300
Subject: [PATCH v[12] 1/1] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
doc/src/sgml/indexam.sgml | 25 +-
src/backend/access/brin/brin.c | 46 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/meson.build | 1 +
src/backend/access/common/options.c | 1367 ++++++++++
src/backend/access/common/reloptions.c | 2278 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 20 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 117 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 1 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 274 ++
src/include/access/reloptions.h | 204 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 148 +-
.../test_oat_hooks/expected/alter_table.out | 16 +
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
47 files changed, 2630 insertions(+), 2354 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 648167045f4..47f697f66a8 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"
@@ -203,7 +204,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern options_spec_set *blrelopt_specset(void);
+extern void blReloptionPostprocess(void *, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index c901e942720..6f1a4dca1c8 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 "commands/vacuum.h"
#include "storage/bufmgr.h"
@@ -31,52 +31,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.
*/
@@ -137,7 +97,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = blcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = bloptions;
+ amroutine->amreloptspecset = blrelopt_specset;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = blvalidate;
@@ -158,6 +118,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* Convert signature length from # of bits to # to words, rounding up */
+ opts->bloomLength = (opts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
+}
+
+
/*
* Fill BloomState structure for particular index.
*/
@@ -472,24 +441,37 @@ BloomInitMetapage(Relation index, ForkNumber forknum)
UnlockReleaseBuffer(metaBuffer);
}
-/*
- * Parse reloptions for bloom index, producing a BloomOptions struct.
- */
-bytea *
-bloptions(Datum reloptions, bool validate)
+options_spec_set *
+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/dblink/dblink.c b/contrib/dblink/dblink.c
index bed2dee3d72..bdd4d83fdf0 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1938,7 +1938,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index bf707c812ed..af5c90a9191 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -202,7 +202,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index d0766f007d2..085a1228951 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -70,7 +70,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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index 08a7c56b5df..98956c5e22f 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
index c50ba60e21c..9e03f202b37 100644
--- a/doc/src/sgml/indexam.sgml
+++ b/doc/src/sgml/indexam.sgml
@@ -504,27 +504,12 @@ amgettreeheight (Relation rel);
<para>
<programlisting>
-bytea *
-amoptions (ArrayType *reloptions,
- bool validate);
+void *
+amreloptspecset(void);
</programlisting>
- Parse and validate the reloptions array for an index. This is called only
- when a non-null reloptions array exists for the index.
- <parameter>reloptions</parameter> is a <type>text</type> array containing entries of the
- form <replaceable>name</replaceable><literal>=</literal><replaceable>value</replaceable>.
- The function should construct a <type>bytea</type> value, which will be copied
- into the <structfield>rd_options</structfield> field of the index's relcache entry.
- The data contents of the <type>bytea</type> value are open for the access
- method to define; most of the standard access methods use struct
- <structname>StdRdOptions</structname>.
- When <parameter>validate</parameter> is true, the function should report a suitable
- error message if any of the options are unrecognized or have invalid
- values; when <parameter>validate</parameter> is false, invalid entries should be
- silently ignored. (<parameter>validate</parameter> is false when loading options
- already stored in <structname>pg_catalog</structname>; an invalid entry could only
- be found if the access method has changed its rules for options, and in
- that case ignoring obsolete entries is appropriate.)
- It is OK to return NULL if default behavior is wanted.
+ Returns pointer to Options Spec Set that defines options available for an
+ index relation. This Spec Set will be used for parsing and validating index
+ relation options.
</para>
<para>
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 75a65ec9c75..8112393c983 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"
@@ -285,7 +284,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = brincostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = brinoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = brinvalidate;
@@ -302,6 +300,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = NULL;
amroutine->amtranslatecmptype = NULL;
+ amroutine->amreloptspecset = bringetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1338,23 +1337,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.
@@ -3013,3 +2995,29 @@ brin_fill_empty_ranges(BrinBuildState *state,
blkno += state->bs_pagesPerRange;
}
}
+
+static options_spec_set *brin_relopt_specset = NULL;
+
+options_spec_set *
+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/Makefile
index e78de312659..014d1b15622 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/meson.build b/src/backend/access/common/meson.build
index e3cdbe7a22e..9f9bbc494cc 100644
--- a/src/backend/access/common/meson.build
+++ b/src/backend/access/common/meson.build
@@ -6,6 +6,7 @@ backend_sources += files(
'detoast.c',
'heaptuple.c',
'indextuple.c',
+ 'options.c',
'printsimple.c',
'printtup.c',
'relation.c',
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 00000000000..fc42a02b573
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1367 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2025, 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 term "specification" instead of "definition" because term
+ * "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 Value 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 values. 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 + offset.
+ */
+
+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 Options 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.
+ *
+ * bytea_size - 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 *namspace, 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 (namspace)
+ {
+ spec_set->namspace = palloc(strlen(namspace) + 1);
+ strcpy(spec_set->namspace, namspace);
+ }
+ else
+ spec_set->namspace = NULL;
+ if (num_items_expected > 0)
+ {
+ spec_set->num_allocated = num_items_expected;
+ spec_set->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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", (int) 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 object 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->assert_on_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->namspace = palloc(strlen(def->defnamespace) + 1);
+ strcpy(option_dst->namspace, def->defnamespace);
+ }
+ else
+ option_dst->namspace = 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_builtin(array, TEXTOID, &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 j;
+ 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 (j = 0; j < text_len; j = j + pg_mblen(text_str))
+ {
+ if (text_str[j] == '=')
+ {
+ name_len = j;
+ 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->namspace = 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_namspaces array. Items without namspace are also accepted
+ *
+ * Used while validation of syntax parser output. Error is thrown if unproper
+ * namespace is found.
+ */
+void
+optionsDefListValdateNamespaces(List *defList,
+ const char * const* allowed_namspaces)
+{
+ 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_namspaces[i])
+ {
+ if (strcmp(def->defnamespace,
+ allowed_namspaces[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 "namspace" namespace. If "namspace"
+ * is NULL, only namespaseless options are returned
+ */
+List *
+optionsDefListFilterNamespaces(List *defList, const char *namspace)
+{
+ ListCell *cell;
+ List *result = NIL;
+
+ foreach(cell, defList)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ if ((!namspace && !def->defnamespace) ||
+ (namspace && def->defnamespace &&
+ strcmp(namspace, 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_builtin(array, TEXTOID, &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 unparsed 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 values 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->namspace && (!spec_set->namspace ||
+ strcmp(spec_set->namspace, option->namspace) != 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter namespace \"%s\"",
+ option->namspace)));
+
+ 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->namspace)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s.%s\"",
+ option->namspace, 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", (int) 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",
+ (int) 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 conversion.
+ */
+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;
+ if (defList)
+ data = optionsValuesToBytea(new_values, spec_set);
+ else
+ data = NULL;
+
+ spec_set->postprocess_fun(data, true);
+ if (data) 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/reloptions.c b/src/backend/access/common/reloptions.c
index 59fb53e7707..45e8eaaee25 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-2025, 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"
@@ -34,6 +31,7 @@
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "storage/bufmgr.h"
/*
* Contents of pg_class.reloptions
@@ -91,408 +89,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_max_threshold",
- "Maximum number of tuple updates or deletes prior to vacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -2, -1, 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
- },
- {
- {
- "vacuum_max_eager_freeze_failure_rate",
- "Fraction of pages in a relation vacuum can scan and fail to freeze before disabling eager scanning.",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, 1.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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -506,17 +104,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -524,225 +113,9 @@ static 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_heap);
+void oid_postvalidate(option_value *value);
/*
* init_local_reloptions
@@ -752,9 +125,8 @@ add_reloption(relopt_gen *newoption)
void
init_local_reloptions(local_relopts *relopts, Size relopt_struct_size)
{
- relopts->options = NIL;
relopts->validators = NIL;
- relopts->relopt_struct_size = relopt_struct_size;
+ relopts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, true, 0);
}
/*
@@ -768,112 +140,6 @@ register_reloptions_validator(local_relopts *relopts, relopts_validator validato
relopts->validators = lappend(relopts->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
@@ -884,47 +150,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);
}
/*
@@ -938,47 +165,8 @@ add_local_int_reloption(local_relopts *relopts, const char *name,
const char *desc, int default_val, int min_val,
int max_val, int offset)
{
- relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
- name, desc, default_val,
- min_val, max_val, 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * init_real_reloption
- * Allocate and initialize a new real reloption
- */
-static relopt_real *
-init_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption;
-
- newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
- name, desc, lockmode);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- return newoption;
-}
-
-/*
- * add_real_reloption
- * Add a new float reloption
- */
-void
-add_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption = init_real_reloption(kinds, name, desc,
- default_val, min_val,
- max_val, lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
}
/*
@@ -987,143 +175,29 @@ add_real_reloption(bits32 kinds, const char *name, const char *desc,
*
* '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;
- }
+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)
+{
+ optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
- return newoption;
}
/*
- * add_string_reloption
- * Add a new string reloption
+ * add_local_enum_reloption
+ * Add a new local enum 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.
+ * 'offset' is offset of int-typed field.
*/
void
-add_string_reloption(bits32 kinds, const char *name, const char *desc,
- const char *default_val, validate_string_relopt validator,
- LOCKMODE lockmode)
+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)
{
- relopt_string *newoption = init_string_reloption(kinds, name, desc,
- default_val,
- validator, NULL,
- lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ members, default_val, detailmsg);
}
/*
@@ -1139,247 +213,8 @@ add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset)
{
- relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- default_val,
- validator, filler,
- 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * Transform a relation options list (list of DefElem) into the text array
- * format that is kept in pg_class.reloptions, including only those options
- * that are in the passed namespace. The output values do not include the
- * namespace.
- *
- * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
- * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
- * reloptions value (possibly NULL), and we replace or remove entries
- * as needed.
- *
- * If acceptOidsOff is true, then we allow oids = false, but throw error when
- * on. This is solely needed for backwards compatibility.
- *
- * Note that this is not responsible for determining whether the options
- * are valid, but it does check that namespaces for all the options given are
- * listed in validnsps. The NULL namespace is always valid and need not be
- * explicitly listed. Passing a NULL pointer means that only the NULL
- * namespace is valid.
- *
- * Both oldOptions and the result are text arrays (or NULL for "default"),
- * but we declare them as Datums to avoid including array.h in reloptions.h.
- */
-Datum
-transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
- const char *const 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_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
-/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
- */
-List *
-untransformRelOptions(Datum options)
-{
- List *result = NIL;
- ArrayType *array;
- Datum *optiondatums;
- int noptions;
- int i;
-
- /* Nothing to do if no options */
- if (!PointerIsValid(DatumGetPointer(options)))
- return result;
-
- array = DatumGetArrayTypeP(options);
-
- deconstruct_array_builtin(array, TEXTOID, &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(p);
- }
- result = lappend(result, makeDefElem(s, val, -1));
- }
-
- return result;
+ optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, offset,
+ NULL, default_val, validator, filler);
}
/*
@@ -1396,12 +231,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,
@@ -1415,548 +251,347 @@ 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_builtin(array, TEXTOID, &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.
+ * 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.
*
- * options is a reloption text array as constructed by transformRelOptions.
- * kind specifies the family of options to be processed.
+ * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
+ * since they are only used by the AV procs and don't change anything
+ * currently executing.
*
- * 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.
+ * Fillfactor can be set because it applies only to subsequent changes made to
+ * data blocks, as documented in heapio.c
*
- * 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).
+ * 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.
*
- * 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.
- */
-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++;
- }
- }
+ * 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.
+ */
+
+
+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)
+ 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);
+
+ optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_vacuum_max_threshold",
+ "Maximum number of tuple updates or deletes prior to vacuum",
+ ShareUpdateExclusiveLock,
+ offsetof(StdRdOptions, autovacuum) +
+ offsetof(AutoVacOpts, vacuum_max_threshold),
+ NULL, -2, -1, INT_MAX);
+
+ optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_vacuum_insert_threshold",
+ "Minimum number of tuple inserts prior to vacuum, or -1 to disable insert vacuums",
+ ShareUpdateExclusiveLock,
+ offsetof(StdRdOptions, autovacuum) +
+ offsetof(AutoVacOpts, vacuum_ins_threshold),
+ NULL, -2, -1, 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\"."));
- *numrelopts = numoptions;
- return reloptions;
-}
-
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
+ 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);
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
+ optionsSpecSetAddReal(stdrd_relopt_spec_set, "vacuum_max_eager_freeze_failure_rate",
+ "Fraction of pages in a relation vacuum can scan and fail to freeze before disabling eager scanning",
+ ShareUpdateExclusiveLock,
+ offsetof(StdRdOptions,
+ vacuum_max_eager_freeze_failure_rate),
+ NULL, -1, 0.0, 1.0);
- if (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
+ 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);
- return values;
+ return stdrd_relopt_spec_set;
}
-/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
- */
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
-
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
- }
+static options_spec_set *heap_relopt_spec_set = NULL;
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
/*
- * 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).
+ * 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 void *
-allocateReloptStruct(Size base, relopt_value *options, int numoptions)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- Size size = base;
- int i;
-
- for (i = 0; i < numoptions; i++)
+ if (data)
{
- relopt_value *optval = &options[i];
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
-
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
-
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-
- 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)
+static options_spec_set *toast_relopt_spec_set = NULL;
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- int i;
- int offset = basesize;
+ if (toast_relopt_spec_set)
+ return toast_relopt_spec_set;
- for (i = 0; i < numoptions; i++)
- {
- int j;
- bool found = false;
+ toast_relopt_spec_set = get_stdrd_relopt_spec_set(false);
+ toast_relopt_spec_set->postprocess_fun = toast_options_postprocess;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+ return toast_relopt_spec_set;
}
/*
- * Option parser for anything that uses StdRdOptions.
+ * Do not allow to set any option on partitioned table
*/
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+static void
+partitioned_options_postprocess(void *data, bool validate)
{
- 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_max_threshold", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_max_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)},
- {"vacuum_max_eager_freeze_failure_rate", RELOPT_TYPE_REAL,
- offsetof(StdRdOptions, vacuum_max_eager_freeze_failure_rate)}
- };
-
- return (bytea *) build_reloptions(reloptions, validate, kind,
- sizeof(StdRdOptions),
- tab, lengthof(tab));
+ if (data && validate)
+ ereport(ERROR,
+ errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot specify storage parameters for a partitioned table"),
+ errhint("Specify storage parameters for its leaf partitions instead."));
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
+options_spec_set *
+get_partitioned_relopt_spec_set(void)
+{
+ if (partitioned_relopt_spec_set)
+ return partitioned_relopt_spec_set;
+ partitioned_relopt_spec_set = get_stdrd_relopt_spec_set(true);
- pfree(options);
+ /* No options for now, so Spec Set is empty */
+ partitioned_relopt_spec_set->postprocess_fun =
+ partitioned_options_postprocess;
- return rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1967,158 +602,165 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
+
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- foreach(lc, relopts->options)
+ if (validate && relopts->validators)
{
- local_relopt *opt = lfirst(lc);
+ int val_count = list_length(values);
+ int i;
+ option_value *val_array;
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ val_array = palloc(sizeof(option_value) * val_count);
- i++;
- }
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (validate)
foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
- if (elems)
- pfree(elems);
+ pfree(val_array);
+ }
return opts;
}
/*
- * Option parser for partitioned tables
- */
-bytea *
-partitioned_table_reloptions(Datum reloptions, bool validate)
-{
- if (validate && reloptions)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot specify storage parameters for a partitioned table"),
- errhint("Specify storage parameters for its leaf partitions instead."));
- return NULL;
-}
-
-/*
- * Option parser for views
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an Options Spec Set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an Options Spec Set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2128,33 +770,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.c
index 0b67108bc34..2f9a38ea284 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/index_selfuncs.h"
#include "utils/rel.h"
#include "utils/typcache.h"
+#include "utils/guc.h"
/*
@@ -73,7 +74,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = ginbuildphasename;
amroutine->amvalidate = ginvalidate;
@@ -88,6 +88,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -602,21 +603,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
*
@@ -728,3 +714,29 @@ ginbuildphasename(int64 phasenum)
return NULL;
}
}
+
+static options_spec_set *gin_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 5482925a0f3..9478b8ca3c0 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -94,7 +94,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = gistcanreturn;
amroutine->amcostestimate = gistcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = gistoptions;
amroutine->amproperty = gistproperty;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = gistvalidate;
@@ -111,6 +110,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = NULL;
amroutine->amtranslatecmptype = gisttranslatecmptype;
+ amroutine->amreloptspecset = gistgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index a6b701943d3..b578031207c 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 "common/pg_prng.h"
#include "storage/indexfsm.h"
#include "utils/float.h"
@@ -908,20 +908,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.
*
@@ -1109,3 +1095,39 @@ gisttranslatecmptype(CompareType cmptype, Oid opfamily)
result = OidFunctionCall1Coll(funcid, InvalidOid, Int32GetDatum(cmptype));
return DatumGetUInt16(result);
}
+
+/* values from GistOptBufferingMode */
+static 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;
+
+options_spec_set *
+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.c
index 4c83b09edde..fdd40998a4f 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -93,7 +93,6 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = hashcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = hashoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = hashvalidate;
@@ -110,6 +109,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = hashtranslatestrategy;
amroutine->amtranslatecmptype = hashtranslatecmptype;
+ amroutine->amreloptspecset = hashgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
index 66c39f60654..8cc1e079200 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 "utils/lsyscache.h"
@@ -271,19 +271,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
*/
@@ -619,3 +606,22 @@ _hash_kill_items(IndexScanDesc scan)
else
_hash_relbuf(rel, buf);
}
+
+static options_spec_set *hash_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 45ea6afba1d..c560a319b87 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -21,6 +21,7 @@
#include "access/nbtree.h"
#include "access/relscan.h"
#include "access/stratnum.h"
+#include "access/options.h"
#include "commands/progress.h"
#include "commands/vacuum.h"
#include "nodes/execnodes.h"
@@ -136,7 +137,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = btcanreturn;
amroutine->amcostestimate = btcostestimate;
amroutine->amgettreeheight = btgettreeheight;
- amroutine->amoptions = btoptions;
amroutine->amproperty = btproperty;
amroutine->ambuildphasename = btbuildphasename;
amroutine->amvalidate = btvalidate;
@@ -153,6 +153,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = btparallelrescan;
amroutine->amtranslatestrategy = bttranslatestrategy;
amroutine->amtranslatecmptype = bttranslatecmptype;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1553,3 +1554,35 @@ bttranslatecmptype(CompareType cmptype, Oid opfamily)
return InvalidStrategy;
}
}
+
+static options_spec_set *bt_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 693e43c674b..cf24295c5cd 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -18,7 +18,8 @@
#include <time.h>
#include "access/nbtree.h"
-#include "access/reloptions.h"
+#include "storage/lock.h"
+#include "access/relscan.h"
#include "commands/progress.h"
#include "miscadmin.h"
#include "utils/datum.h"
@@ -2715,23 +2716,6 @@ BTreeShmemInit(void)
Assert(found);
}
-bytea *
-btoptions(Datum reloptions, bool validate)
-{
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(BTOptions, fillfactor)},
- {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
- offsetof(BTOptions, vacuum_cleanup_index_scale_factor)},
- {"deduplicate_items", RELOPT_TYPE_BOOL,
- offsetof(BTOptions, deduplicate_items)}
- };
-
- return (bytea *) build_reloptions(reloptions, validate,
- RELOPT_KIND_BTREE,
- sizeof(BTOptions),
- tab, lengthof(tab));
-}
-
/*
* btproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 7e56b1e6b95..e81af09669f 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"
@@ -79,7 +79,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = spgcanreturn;
amroutine->amcostestimate = spgcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = spgoptions;
amroutine->amproperty = spgproperty;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = spgvalidate;
@@ -96,6 +95,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = NULL;
amroutine->amtranslatecmptype = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -751,22 +751,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).
@@ -1365,3 +1349,24 @@ spgproperty(Oid index_oid, int attno,
return true;
}
+
+static options_spec_set *spgist_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 44b4665ccd3..d59285b074e 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -85,6 +85,7 @@ create_ctas_internal(List *attrList, IntoClause *into)
Datum toast_options;
const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
ObjectAddress intoRelationAddr;
+ List *toastDefList;
/* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
is_matview = (into->viewQuery != NULL);
@@ -119,14 +120,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.c
index c14e038d54f..1c6426e6c57 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -112,7 +112,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 32ff3ca9a28..d68dcb3e299 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -20,6 +20,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"
@@ -570,8 +571,8 @@ DefineIndex(Oid tableId,
IndexAmRoutine *amRoutine;
bool amcanorder;
bool amissummarizing;
- amoptions_function amoptions;
bool exclusion;
+ amreloptspecset_function amreloptspecsetfn;
bool partitioned;
bool safe_index;
Datum reloptions;
@@ -893,7 +894,7 @@ DefineIndex(Oid tableId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
amissummarizing = amRoutine->amsummarizing;
pfree(amRoutine);
@@ -908,10 +909,18 @@ DefineIndex(Oid tableId,
/*
* 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.
@@ -2246,8 +2255,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
Assert(attn < nkeycols);
opclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
else
opclassOptions[attn] = (Datum) 0;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 59156a1c1f6..057c5094e67 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -21,6 +21,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"
@@ -739,7 +740,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
ListCell *listptr;
AttrNumber attnum;
bool partitioned;
- const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
LOCKMODE parentLockmode;
@@ -888,19 +888,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);
- break;
+ {
+ /* If it is not all listed above, then it if heap */
+ const char *const 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 */
+ const char *const 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)
@@ -4512,7 +4529,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);
@@ -4554,7 +4571,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.
@@ -4775,9 +4792,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8934,12 +8956,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));
@@ -15804,12 +15827,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];
- const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
+ List *optionsDefList;
+ options_spec_set *optionsSpecSet;
+ const char *const heap_namespaces[] = HEAP_RELOPT_NAMESPACES;
if (defList == NIL && operation != AT_ReplaceRelOptions)
return; /* nothing to do */
@@ -15829,38 +15853,56 @@ 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))));
+ 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),
@@ -15870,11 +15912,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;
@@ -15950,20 +15996,23 @@ 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.c
index a9005cc7212..5dc733905be 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -333,10 +333,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
@@ -1052,11 +1051,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.c
index f0835fc3070..87780f32309 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -80,7 +80,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
if (isnull)
fdw->options = NIL;
else
- fdw->options = untransformRelOptions(datum);
+ fdw->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -167,7 +167,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
if (isnull)
server->options = NIL;
else
- server->options = untransformRelOptions(datum);
+ server->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -239,7 +239,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -276,7 +276,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -309,7 +309,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -526,7 +526,7 @@ pg_options_to_table(PG_FUNCTION_ARGS)
List *options;
ReturnSetInfo *rsinfo;
- options = untransformRelOptions(array);
+ options = optionsTextArrayToDefList(array);
rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
/* prepare the result set */
@@ -624,7 +624,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index abbe1bb45a3..51c3fffe3e8 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1925,7 +1925,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;
@@ -1989,7 +1989,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 25fe3d58016..56cc463ee0e 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1159,6 +1159,7 @@ ProcessUtilitySlow(ParseState *pstate,
CreateStmt *cstmt = (CreateStmt *) stmt;
Datum toast_options;
const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
+ List *toastDefList;
/* Remember transformed RangeVar for LIKE */
table_rv = cstmt->relation;
@@ -1182,15 +1183,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);
@@ -1299,9 +1301,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 5c8360c08b5..f2850d5fc38 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"
@@ -176,7 +177,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index d1ae761b3f6..07a86a85577 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -465,7 +465,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -480,11 +480,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;
@@ -495,7 +495,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index 23458599298..3010692c442 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,8 @@ get_tablespace(Oid spcid)
opts = NULL;
else
{
- bytea *bytea_opts = tablespace_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_tablespace_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts));
memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index bf729a1e4ae..09775d74666 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -15,6 +15,7 @@
#include "access/cmptype.h"
#include "access/genam.h"
#include "access/stratnum.h"
+#include "access/options.h"
/*
* We don't wish to include planner header files here, since most of an index
@@ -159,9 +160,9 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
*/
typedef int (*amgettreeheight_function) (Relation rel);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
+
+/* get Spec Set for relation options */
+typedef options_spec_set *(*amreloptspecset_function) ();
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
@@ -296,7 +297,7 @@ typedef struct IndexAmRoutine
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
amgettreeheight_function amgettreeheight; /* can be NULL */
- amoptions_function amoptions;
+ amreloptspecset_function amreloptspecset; /* can be NULL */
amproperty_function amproperty; /* can be NULL */
ambuildphasename_function ambuildphasename; /* can be NULL */
amvalidate_function amvalidate;
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 821f1e02806..88434a55612 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -37,6 +37,8 @@ typedef struct BrinStatsData
#define BRIN_DEFAULT_PAGES_PER_RANGE 128
+#define BRIN_MIN_PAGES_PER_RANGE 1
+#define BRIN_MAX_PAGES_PER_RANGE 131072
#define BrinGetPagesPerRange(relation) \
(AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
relation->rd_rel->relam == BRIN_AM_OID), \
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index d093a0bf130..2513250fe6a 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -109,6 +109,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern options_spec_set *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 95d8805b66f..6fc90db0b18 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -110,6 +110,7 @@ extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
extern char *ginbuildphasename(int64 phasenum);
+extern options_spec_set *gingetreloptspecset(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 39404ec7cdb..7ed2230e0be 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
*/
@@ -479,7 +481,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
#define GIST_MIN_FILLFACTOR 10
#define GIST_DEFAULT_FILLFACTOR 90
-extern bytea *gistoptions(Datum reloptions, bool validate);
+extern options_spec_set *gistgetreloptspecset(void);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 073ad29b19b..3dccf6ed279 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -477,6 +476,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 options_spec_set *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index e4fdeca3402..d86f150e50e 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1307,7 +1307,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 options_spec_set *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 00000000000..c1df04c90f4
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,274 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2025, 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;
+
+/*
+ * Each Option Value item passes through certain life cycle. Option Value
+ * Status specifies at what point of life cycle this Option Value is now.
+ * Option Value Statuses are:
+ * EMPTY - Option Value structure have been just created
+ * RAW - Option Value have been read, but has not been parsed or validated yet.
+ * Use raw_value structure member to access Raw Value
+ * PARSED - Option Value have been parsed, you should use proper union member
+ * to access Parsed Option Value, according to Option Type
+ * FOR_RESET - Specifies that this Option is designated for resetting to
+ * default value
+ */
+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 Option
+ * Spec Set 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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 *namspace; /* 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 *namspace;
+ 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 *namspace,
+ 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,
+ const char * const* allowed_namspaces);
+extern List *optionsDefListFilterNamespaces(List *defList,
+ const char *namspace);
+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.h
index 43445cdcc6c..b42bb614e03 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-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -25,175 +20,36 @@
#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 */
+ 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 *relopts, Size relopt_struct_size);
extern void register_reloptions_validator(local_relopts *relopts,
@@ -210,7 +66,7 @@ extern void add_local_real_reloption(local_relopts *relopts, 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 *relopts, const char *name,
@@ -219,29 +75,17 @@ extern void add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, const char *const validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index cbe9b347d8f..c92c8698b98 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -189,9 +189,6 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
-/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
-
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index cb43a278f46..9f78a726646 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -530,6 +530,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 options_spec_set *spggetreloptspecset(void);
/* spgdoinsert.c */
extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 6832470d387..03a656cb4a8 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -36,7 +36,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index c83954ad11d..71432a1b891 100644
--- a/src/test/modules/dummy_index_am/dummy_index_am.c
+++ b/src/test/modules/dummy_index_am/dummy_index_am.c
@@ -14,18 +14,14 @@
#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"
PG_MODULE_MAGIC;
-/* parse table for fillRelOptions */
-static relopt_parse_elt di_relopt_tab[6];
-
-/* Kind of relation options for dummy index */
-static relopt_kind di_relopt_kind;
+void _PG_init(void);
typedef enum DummyAmEnum
{
@@ -45,7 +41,7 @@ typedef struct DummyIndexOptions
int option_string_null_offset;
} DummyIndexOptions;
-static relopt_enum_elt_def dummyAmEnumValues[] =
+static opt_enum_elt_def dummyAmEnumValues[] =
{
{"one", DUMMY_AM_ENUM_ONE},
{"two", DUMMY_AM_ENUM_TWO},
@@ -59,77 +55,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;
+options_spec_set *digetreloptspecset(void);
+
+options_spec_set *
+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.
*/
@@ -214,19 +215,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 +297,6 @@ dihandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = dicostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = dioptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = divalidate;
@@ -323,12 +310,7 @@ dihandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = digetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
-
-void
-_PG_init(void)
-{
- create_reloptions_table();
-}
diff --git a/src/test/modules/test_oat_hooks/expected/alter_table.out b/src/test/modules/test_oat_hooks/expected/alter_table.out
index 8cbacca2c9b..8319cd06941 100644
--- a/src/test/modules/test_oat_hooks/expected/alter_table.out
+++ b/src/test/modules/test_oat_hooks/expected/alter_table.out
@@ -82,6 +82,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -89,6 +91,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -96,6 +100,8 @@ ALTER TABLE test_oat_schema.test_oat_tab FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -103,6 +109,8 @@ ALTER TABLE test_oat_schema.test_oat_tab NO FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -111,6 +119,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -120,6 +130,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -130,6 +142,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -139,6 +153,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
index 9de19b4e3f1..67f0a828f9f 100644
--- a/src/test/regress/expected/reloptions.out
+++ b/src/test/regress/expected/reloptions.out
@@ -175,7 +175,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
@@ -194,6 +194,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.sql
index 24fbe0b478d..9b404295c05 100644
--- a/src/test/regress/sql/reloptions.sql
+++ b/src/test/regress/sql/reloptions.sql
@@ -112,6 +112,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
--
--
2.39.2
В письме от четверг, 6 марта 2025 г. 19:12:54 MSK пользователь Nikolay Shaplov
написал:
Rebased version of the patch
--
Nikolay Shaplov aka Nataraj
Fuzzing Engineer at Postgres Professional
Matrix IM: @dhyan:nataraj.su
Attachments:
v-13-0001-New-options-engine.patchtext/x-patch; charset=unicode-2-0-utf-8; name=v-13-0001-New-options-engine.patchDownload
From 8cf902e5bfde43a9788776a61b924218e741c0a1 Mon Sep 17 00:00:00 2001
From: Nikolay Shaplov <dhyan@nataraj.su>
Date: Wed, 26 Feb 2025 22:24:29 +0300
Subject: [PATCH v[13] 1/1] New options engine
Add new options engine that will allow to define options directly in any part
of code they are needed (anywhere, not only for relation and op. classes) and
use unified API for option processing.
This will allow:
- Unify options code. Now we have core AM reloptions, contrib AM reloptions and
local reloptions for op. classes, each using their own API. New engine will
allow to use single API for them all.
- Move options definition completely to Access Method implementation. Done for
index AM, will be done storage AM later. (Before core AM all options were
defined in reloptions.c)
- All options for AM is defined in one single chunk of code. (Before for core AM
you had to edit both reloptions.c and code of AM, this was error prone)
- More simple way to define new option stets (we will need partitioned.* options
sooner or later, this engine will allow to do it more easy)
- Allow to add name=value options anywhere they needed.
- More flexible way for defining option behavior (e.g. disable OIDS=true via
postvalidate callback, without changing option processing code)
What is done, and what is still in progress:
- Index AM options now using new option engine the way it should be.
- Options for Heap and Toast, are also using new engine, but they are still
located in reloptions.c file as they were before. They will be moved to
storage AM later.
- local_options API that is used for op. class options is now wrapper around new
option engine. Later on, build in op. classes will be updated to use new
option engine directly, and local_options API can be kept for backward
compatibility for a while.
This patch does not change postgres behavior. All existing options work the way
they did it before. One single error message is changed, and it is more
accurate now.
```
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
```
General concepts of the new engine:
- `option_spec` is a C-structure that completely describes single option, how it
should be parsed, validated and stored: name, type, acceptable values, offset
in bytea representation, postvalidate call back, etc. We have `option_spec_*`
for each option type.
- `options_spec_set` is a set of `option_spec`s that are available for certain
context (e.g. for certain AM reloptions). It is a list of option_spec`s plus a
callback for overall validation (e.g. make sure optionA < optionB)
- There are four representations of options. The way they came from SQL parser
(defList), the way they are stored in DB (TEXT[]), the binary representation
that is used by outer code (bytea), and internal representation that is used
by option engine while parsing and validating options (Option Value List)
- There are functions to convert options from one representation to another
(possibly with validation if applicable): `optionsTextArrayToDefList`,
`optionsDefListToTextArray`, `optionsTextArrayToBytea` and others.
- There are functions for defining `option_spec_set`: `allocateOptionsSpecSet`,
`optionsSpecSetAddBool`, `optionsSpecSetAddInt`, etc.
Outer code, that would like to use option engine, should define it's own
`option_spec_set`, implement getting options from SQL parser, getting and
putting them to DB storage, passing binary representation of the option to the
code that would actually use them. For parsing, validating and converting
options from one representation to another, outer code should use functions
from options engine function set. Each function will use information from
`option_spec_set` to determinate what options are available and how exactly they
should be parsed and validated.
This is only a start of option code reworking. Further improvements listed
above and other suggestion will follow.
---
contrib/bloom/bloom.h | 4 +-
contrib/bloom/blutils.c | 102 +-
contrib/dblink/dblink.c | 2 +-
contrib/file_fdw/file_fdw.c | 2 +-
contrib/postgres_fdw/option.c | 2 +-
contrib/test_decoding/expected/twophase.out | 3 +-
doc/src/sgml/indexam.sgml | 25 +-
src/backend/access/brin/brin.c | 46 +-
src/backend/access/common/Makefile | 1 +
src/backend/access/common/meson.build | 1 +
src/backend/access/common/options.c | 1367 ++++++++++
src/backend/access/common/reloptions.c | 2278 ++++-------------
src/backend/access/gin/ginutil.c | 46 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 52 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/hash/hashutil.c | 34 +-
src/backend/access/nbtree/nbtree.c | 35 +-
src/backend/access/nbtree/nbtutils.c | 20 +-
src/backend/access/spgist/spgutils.c | 41 +-
src/backend/commands/createas.c | 11 +-
src/backend/commands/foreigncmds.c | 2 +-
src/backend/commands/indexcmds.c | 22 +-
src/backend/commands/tablecmds.c | 97 +-
src/backend/commands/tablespace.c | 16 +-
src/backend/foreign/foreign.c | 14 +-
src/backend/parser/parse_utilcmd.c | 4 +-
src/backend/tcop/utility.c | 29 +-
src/backend/utils/cache/attoptcache.c | 4 +-
src/backend/utils/cache/relcache.c | 8 +-
src/backend/utils/cache/spccache.c | 3 +-
src/include/access/amapi.h | 9 +-
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 1 +
src/include/access/gin_private.h | 1 +
src/include/access/gist_private.h | 4 +-
src/include/access/hash.h | 2 +-
src/include/access/nbtree.h | 2 +-
src/include/access/options.h | 274 ++
src/include/access/reloptions.h | 204 +-
src/include/access/spgist.h | 3 -
src/include/access/spgist_private.h | 1 +
src/include/commands/tablecmds.h | 2 +-
.../modules/dummy_index_am/dummy_index_am.c | 148 +-
.../test_oat_hooks/expected/alter_table.out | 16 +
src/test/regress/expected/reloptions.out | 13 +-
src/test/regress/sql/reloptions.sql | 7 +
47 files changed, 2616 insertions(+), 2348 deletions(-)
create mode 100644 src/backend/access/common/options.c
create mode 100644 src/include/access/options.h
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 648167045f4..47f697f66a8 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"
@@ -203,7 +204,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern options_spec_set *blrelopt_specset(void);
+extern void blReloptionPostprocess(void *, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 2c0e71eedc6..e0c1bf2952d 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 "commands/vacuum.h"
#include "storage/bufmgr.h"
@@ -31,52 +31,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.
*/
@@ -138,7 +98,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = blcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = bloptions;
+ amroutine->amreloptspecset = blrelopt_specset;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = blvalidate;
@@ -159,6 +119,15 @@ blhandler(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(amroutine);
}
+void
+blReloptionPostprocess(void *data, bool validate)
+{
+ BloomOptions *opts = (BloomOptions *) data;
+ /* Convert signature length from # of bits to # to words, rounding up */
+ opts->bloomLength = (opts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
+}
+
+
/*
* Fill BloomState structure for particular index.
*/
@@ -473,24 +442,37 @@ BloomInitMetapage(Relation index, ForkNumber forknum)
UnlockReleaseBuffer(metaBuffer);
}
-/*
- * Parse reloptions for bloom index, producing a BloomOptions struct.
- */
-bytea *
-bloptions(Datum reloptions, bool validate)
+options_spec_set *
+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/dblink/dblink.c b/contrib/dblink/dblink.c
index 58c1a6221c8..75353a45029 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1936,7 +1936,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 56ececac70b..7bac6b314f6 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -203,7 +203,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index d0766f007d2..085a1228951 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -70,7 +70,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/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index 08a7c56b5df..98956c5e22f 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
AND relation = 'test_prepared1'::regclass;
relation | locktype | mode
-----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
test_prepared_1 | relation | RowExclusiveLock
test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
-- The insert should show the newly altered column but not the DDL.
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
index 768b77aa0d2..d416882b5f5 100644
--- a/doc/src/sgml/indexam.sgml
+++ b/doc/src/sgml/indexam.sgml
@@ -506,27 +506,12 @@ amgettreeheight (Relation rel);
<para>
<programlisting>
-bytea *
-amoptions (ArrayType *reloptions,
- bool validate);
+void *
+amreloptspecset(void);
</programlisting>
- Parse and validate the reloptions array for an index. This is called only
- when a non-null reloptions array exists for the index.
- <parameter>reloptions</parameter> is a <type>text</type> array containing entries of the
- form <replaceable>name</replaceable><literal>=</literal><replaceable>value</replaceable>.
- The function should construct a <type>bytea</type> value, which will be copied
- into the <structfield>rd_options</structfield> field of the index's relcache entry.
- The data contents of the <type>bytea</type> value are open for the access
- method to define; most of the standard access methods use struct
- <structname>StdRdOptions</structname>.
- When <parameter>validate</parameter> is true, the function should report a suitable
- error message if any of the options are unrecognized or have invalid
- values; when <parameter>validate</parameter> is false, invalid entries should be
- silently ignored. (<parameter>validate</parameter> is false when loading options
- already stored in <structname>pg_catalog</structname>; an invalid entry could only
- be found if the access method has changed its rules for options, and in
- that case ignoring obsolete entries is appropriate.)
- It is OK to return NULL if default behavior is wanted.
+ Returns pointer to Options Spec Set that defines options available for an
+ index relation. This Spec Set will be used for parsing and validating index
+ relation options.
</para>
<para>
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 737ad638808..eb32716c34e 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"
@@ -286,7 +285,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = brincostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = brinoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = brinvalidate;
@@ -303,6 +301,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = NULL;
amroutine->amtranslatecmptype = NULL;
+ amroutine->amreloptspecset = bringetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1341,23 +1340,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.
@@ -3016,3 +2998,29 @@ brin_fill_empty_ranges(BrinBuildState *state,
blkno += state->bs_pagesPerRange;
}
}
+
+static options_spec_set *brin_relopt_specset = NULL;
+
+options_spec_set *
+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/Makefile
index e78de312659..014d1b15622 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/meson.build b/src/backend/access/common/meson.build
index e3cdbe7a22e..9f9bbc494cc 100644
--- a/src/backend/access/common/meson.build
+++ b/src/backend/access/common/meson.build
@@ -6,6 +6,7 @@ backend_sources += files(
'detoast.c',
'heaptuple.c',
'indextuple.c',
+ 'options.c',
'printsimple.c',
'printtup.c',
'relation.c',
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 00000000000..fc42a02b573
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1367 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2025, 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 term "specification" instead of "definition" because term
+ * "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 Value 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 values. 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 + offset.
+ */
+
+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 Options 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.
+ *
+ * bytea_size - 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 *namspace, 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 (namspace)
+ {
+ spec_set->namspace = palloc(strlen(namspace) + 1);
+ strcpy(spec_set->namspace, namspace);
+ }
+ else
+ spec_set->namspace = NULL;
+ if (num_items_expected > 0)
+ {
+ spec_set->num_allocated = num_items_expected;
+ spec_set->assert_on_realloc = true;
+ spec_set->definitions = palloc(
+ spec_set->num_allocated * sizeof(option_spec_basic *));
+ }
+ else
+ {
+ spec_set->num_allocated = 0;
+ spec_set->assert_on_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", (int) 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 object 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->assert_on_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->namspace = palloc(strlen(def->defnamespace) + 1);
+ strcpy(option_dst->namspace, def->defnamespace);
+ }
+ else
+ option_dst->namspace = 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_builtin(array, TEXTOID, &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 j;
+ 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 (j = 0; j < text_len; j = j + pg_mblen(text_str))
+ {
+ if (text_str[j] == '=')
+ {
+ name_len = j;
+ 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->namspace = 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_namspaces array. Items without namspace are also accepted
+ *
+ * Used while validation of syntax parser output. Error is thrown if unproper
+ * namespace is found.
+ */
+void
+optionsDefListValdateNamespaces(List *defList,
+ const char * const* allowed_namspaces)
+{
+ 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_namspaces[i])
+ {
+ if (strcmp(def->defnamespace,
+ allowed_namspaces[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 "namspace" namespace. If "namspace"
+ * is NULL, only namespaseless options are returned
+ */
+List *
+optionsDefListFilterNamespaces(List *defList, const char *namspace)
+{
+ ListCell *cell;
+ List *result = NIL;
+
+ foreach(cell, defList)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ if ((!namspace && !def->defnamespace) ||
+ (namspace && def->defnamespace &&
+ strcmp(namspace, 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_builtin(array, TEXTOID, &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 unparsed 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 values 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->namspace && (!spec_set->namspace ||
+ strcmp(spec_set->namspace, option->namspace) != 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter namespace \"%s\"",
+ option->namspace)));
+
+ 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->namspace)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s.%s\"",
+ option->namspace, 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", (int) 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",
+ (int) 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 conversion.
+ */
+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;
+ if (defList)
+ data = optionsValuesToBytea(new_values, spec_set);
+ else
+ data = NULL;
+
+ spec_set->postprocess_fun(data, true);
+ if (data) 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/reloptions.c b/src/backend/access/common/reloptions.c
index 59fb53e7707..45e8eaaee25 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-2025, 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"
@@ -34,6 +31,7 @@
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "storage/bufmgr.h"
/*
* Contents of pg_class.reloptions
@@ -91,408 +89,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_max_threshold",
- "Maximum number of tuple updates or deletes prior to vacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -2, -1, 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
- },
- {
- {
- "vacuum_max_eager_freeze_failure_rate",
- "Fraction of pages in a relation vacuum can scan and fail to freeze before disabling eager scanning.",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, 1.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 */
-static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
+static opt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
@@ -506,17 +104,8 @@ static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{(const char *) NULL} /* list terminator */
};
-/* values from GistOptBufferingMode */
-static 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 */
-static relopt_enum_elt_def viewCheckOptValues[] =
+static opt_enum_elt_def viewCheckOptValues[] =
{
/* no value for NOT_SET */
{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
@@ -524,225 +113,9 @@ static 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_heap);
+void oid_postvalidate(option_value *value);
/*
* init_local_reloptions
@@ -752,9 +125,8 @@ add_reloption(relopt_gen *newoption)
void
init_local_reloptions(local_relopts *relopts, Size relopt_struct_size)
{
- relopts->options = NIL;
relopts->validators = NIL;
- relopts->relopt_struct_size = relopt_struct_size;
+ relopts->spec_set = allocateOptionsSpecSet(NULL, relopt_struct_size, true, 0);
}
/*
@@ -768,112 +140,6 @@ register_reloptions_validator(local_relopts *relopts, relopts_validator validato
relopts->validators = lappend(relopts->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
@@ -884,47 +150,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);
}
/*
@@ -938,47 +165,8 @@ add_local_int_reloption(local_relopts *relopts, const char *name,
const char *desc, int default_val, int min_val,
int max_val, int offset)
{
- relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
- name, desc, default_val,
- min_val, max_val, 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * init_real_reloption
- * Allocate and initialize a new real reloption
- */
-static relopt_real *
-init_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption;
-
- newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
- name, desc, lockmode);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- return newoption;
-}
-
-/*
- * add_real_reloption
- * Add a new float reloption
- */
-void
-add_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val,
- LOCKMODE lockmode)
-{
- relopt_real *newoption = init_real_reloption(kinds, name, desc,
- default_val, min_val,
- max_val, lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddInt(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
}
/*
@@ -987,143 +175,29 @@ add_real_reloption(bits32 kinds, const char *name, const char *desc,
*
* '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;
- }
+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)
+{
+ optionsSpecSetAddReal(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ default_val, min_val, max_val);
- return newoption;
}
/*
- * add_string_reloption
- * Add a new string reloption
+ * add_local_enum_reloption
+ * Add a new local enum 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.
+ * 'offset' is offset of int-typed field.
*/
void
-add_string_reloption(bits32 kinds, const char *name, const char *desc,
- const char *default_val, validate_string_relopt validator,
- LOCKMODE lockmode)
+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)
{
- relopt_string *newoption = init_string_reloption(kinds, name, desc,
- default_val,
- validator, NULL,
- lockmode);
-
- add_reloption((relopt_gen *) newoption);
+ optionsSpecSetAddEnum(relopts->spec_set, name, desc, NoLock, offset, NULL,
+ members, default_val, detailmsg);
}
/*
@@ -1139,247 +213,8 @@ add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset)
{
- relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
- name, desc,
- default_val,
- validator, filler,
- 0);
-
- add_local_reloption(relopts, (relopt_gen *) newoption, offset);
-}
-
-/*
- * Transform a relation options list (list of DefElem) into the text array
- * format that is kept in pg_class.reloptions, including only those options
- * that are in the passed namespace. The output values do not include the
- * namespace.
- *
- * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
- * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
- * reloptions value (possibly NULL), and we replace or remove entries
- * as needed.
- *
- * If acceptOidsOff is true, then we allow oids = false, but throw error when
- * on. This is solely needed for backwards compatibility.
- *
- * Note that this is not responsible for determining whether the options
- * are valid, but it does check that namespaces for all the options given are
- * listed in validnsps. The NULL namespace is always valid and need not be
- * explicitly listed. Passing a NULL pointer means that only the NULL
- * namespace is valid.
- *
- * Both oldOptions and the result are text arrays (or NULL for "default"),
- * but we declare them as Datums to avoid including array.h in reloptions.h.
- */
-Datum
-transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
- const char *const 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_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (strcmp(def->defnamespace, validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (strcmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
-
- /*
- * This is not a great place for this test, but there's no other
- * convenient place to filter the option out. As WITH (oids =
- * false) will be removed someday, this seems like an acceptable
- * amount of ugly.
- */
- if (acceptOidsOff && def->defnamespace == NULL &&
- strcmp(def->defname, "oids") == 0)
- {
- if (defGetBoolean(def))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("tables declared WITH OIDS are not supported")));
- /* skip over option, reloptions machinery doesn't know it */
- continue;
- }
-
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
-/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
- */
-List *
-untransformRelOptions(Datum options)
-{
- List *result = NIL;
- ArrayType *array;
- Datum *optiondatums;
- int noptions;
- int i;
-
- /* Nothing to do if no options */
- if (!PointerIsValid(DatumGetPointer(options)))
- return result;
-
- array = DatumGetArrayTypeP(options);
-
- deconstruct_array_builtin(array, TEXTOID, &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(p);
- }
- result = lappend(result, makeDefElem(s, val, -1));
- }
-
- return result;
+ optionsSpecSetAddString(relopts->spec_set, name, desc, NoLock, offset,
+ NULL, default_val, validator, filler);
}
/*
@@ -1396,12 +231,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,
@@ -1415,548 +251,347 @@ 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_builtin(array, TEXTOID, &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.
+ * 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.
*
- * options is a reloption text array as constructed by transformRelOptions.
- * kind specifies the family of options to be processed.
+ * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
+ * since they are only used by the AV procs and don't change anything
+ * currently executing.
*
- * 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.
+ * Fillfactor can be set because it applies only to subsequent changes made to
+ * data blocks, as documented in heapio.c
*
- * 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).
+ * 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.
*
- * 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.
- */
-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++;
- }
- }
+ * 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.
+ */
+
+
+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)
+ 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);
+
+ optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_vacuum_max_threshold",
+ "Maximum number of tuple updates or deletes prior to vacuum",
+ ShareUpdateExclusiveLock,
+ offsetof(StdRdOptions, autovacuum) +
+ offsetof(AutoVacOpts, vacuum_max_threshold),
+ NULL, -2, -1, INT_MAX);
+
+ optionsSpecSetAddInt(stdrd_relopt_spec_set, "autovacuum_vacuum_insert_threshold",
+ "Minimum number of tuple inserts prior to vacuum, or -1 to disable insert vacuums",
+ ShareUpdateExclusiveLock,
+ offsetof(StdRdOptions, autovacuum) +
+ offsetof(AutoVacOpts, vacuum_ins_threshold),
+ NULL, -2, -1, 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\"."));
- *numrelopts = numoptions;
- return reloptions;
-}
-
-/* Parse local unregistered options. */
-static relopt_value *
-parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
-{
- int nopts = list_length(relopts->options);
- relopt_value *values = palloc(sizeof(*values) * nopts);
- ListCell *lc;
- int i = 0;
+ 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);
- foreach(lc, relopts->options)
- {
- local_relopt *opt = lfirst(lc);
-
- values[i].gen = opt->option;
- values[i].isset = false;
-
- i++;
- }
+ optionsSpecSetAddReal(stdrd_relopt_spec_set, "vacuum_max_eager_freeze_failure_rate",
+ "Fraction of pages in a relation vacuum can scan and fail to freeze before disabling eager scanning",
+ ShareUpdateExclusiveLock,
+ offsetof(StdRdOptions,
+ vacuum_max_eager_freeze_failure_rate),
+ NULL, -1, 0.0, 1.0);
- if (options != (Datum) 0)
- parseRelOptionsInternal(options, validate, values, nopts);
+ 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);
- return values;
+ return stdrd_relopt_spec_set;
}
-/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
- */
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
-
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_ENUM:
- {
- relopt_enum *optenum = (relopt_enum *) option->gen;
- relopt_enum_elt_def *elt;
-
- parsed = false;
- for (elt = optenum->members; elt->string_val; elt++)
- {
- if (pg_strcasecmp(value, elt->string_val) == 0)
- {
- option->values.enum_val = elt->symbol_val;
- parsed = true;
- break;
- }
- }
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for enum option \"%s\": %s",
- option->gen->name, value),
- optenum->detailmsg ?
- errdetail_internal("%s", _(optenum->detailmsg)) : 0));
-
- /*
- * If value is not among the allowed string values, but we are
- * not asked to validate, just use the default numeric value.
- */
- if (!parsed)
- option->values.enum_val = optenum->default_val;
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
- }
+static options_spec_set *heap_relopt_spec_set = NULL;
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+options_spec_set *
+get_heap_relopt_spec_set(void)
+{
+ if (heap_relopt_spec_set)
+ return heap_relopt_spec_set;
+ heap_relopt_spec_set = get_stdrd_relopt_spec_set(true);
+ return heap_relopt_spec_set;
}
/*
- * 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).
+ * 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 void *
-allocateReloptStruct(Size base, relopt_value *options, int numoptions)
+static void
+toast_options_postprocess(void *data, bool validate)
{
- Size size = base;
- int i;
-
- for (i = 0; i < numoptions; i++)
+ if (data)
{
- relopt_value *optval = &options[i];
+ StdRdOptions *toast_options = (StdRdOptions *) data;
- if (optval->gen->type == RELOPT_TYPE_STRING)
- {
- relopt_string *optstr = (relopt_string *) optval->gen;
-
- if (optstr->fill_cb)
- {
- const char *val = optval->isset ? optval->values.string_val :
- optstr->default_isnull ? NULL : optstr->default_val;
-
- size += optstr->fill_cb(val, NULL);
- }
- else
- size += GET_STRING_RELOPTION_LEN(*optval) + 1;
- }
+ toast_options->fillfactor = 100;
+ toast_options->autovacuum.analyze_threshold = -1;
+ toast_options->autovacuum.analyze_scale_factor = -1;
}
-
- 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)
+static options_spec_set *toast_relopt_spec_set = NULL;
+options_spec_set *
+get_toast_relopt_spec_set(void)
{
- int i;
- int offset = basesize;
+ if (toast_relopt_spec_set)
+ return toast_relopt_spec_set;
- for (i = 0; i < numoptions; i++)
- {
- int j;
- bool found = false;
+ toast_relopt_spec_set = get_stdrd_relopt_spec_set(false);
+ toast_relopt_spec_set->postprocess_fun = toast_options_postprocess;
- for (j = 0; j < numelems; j++)
- {
- if (strcmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_ENUM:
- *(int *) itempos = options[i].isset ?
- options[i].values.enum_val :
- ((relopt_enum *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (optstring->fill_cb)
- {
- Size size =
- optstring->fill_cb(string_val,
- (char *) rdopts + offset);
-
- if (size)
- {
- *(int *) itempos = offset;
- offset += size;
- }
- else
- *(int *) itempos = 0;
- }
- else if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+ return toast_relopt_spec_set;
}
/*
- * Option parser for anything that uses StdRdOptions.
+ * Do not allow to set any option on partitioned table
*/
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+static void
+partitioned_options_postprocess(void *data, bool validate)
{
- 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_max_threshold", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_max_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)},
- {"vacuum_max_eager_freeze_failure_rate", RELOPT_TYPE_REAL,
- offsetof(StdRdOptions, vacuum_max_eager_freeze_failure_rate)}
- };
-
- return (bytea *) build_reloptions(reloptions, validate, kind,
- sizeof(StdRdOptions),
- tab, lengthof(tab));
+ if (data && validate)
+ ereport(ERROR,
+ errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot specify storage parameters for a partitioned table"),
+ errhint("Specify storage parameters for its leaf partitions instead."));
}
-/*
- * 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.
- */
-void *
-build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems)
-{
- int numoptions;
- relopt_value *options;
- void *rdopts;
-
- /* parse options specific to given relation option kind */
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
- Assert(numoptions <= num_relopt_elems);
- /* if none set, we're done */
- if (numoptions == 0)
- {
- Assert(options == NULL);
- return NULL;
- }
+static options_spec_set *partitioned_relopt_spec_set = NULL;
- /* allocate and fill the structure */
- rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
- fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
- validate, relopt_elems, num_relopt_elems);
+options_spec_set *
+get_partitioned_relopt_spec_set(void)
+{
+ if (partitioned_relopt_spec_set)
+ return partitioned_relopt_spec_set;
+ partitioned_relopt_spec_set = get_stdrd_relopt_spec_set(true);
- pfree(options);
+ /* No options for now, so Spec Set is empty */
+ partitioned_relopt_spec_set->postprocess_fun =
+ partitioned_options_postprocess;
- return rdopts;
+ return partitioned_relopt_spec_set;
}
/*
@@ -1967,158 +602,165 @@ build_reloptions(Datum reloptions, bool validate,
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;
+ List *values;
+
+ values = optionsTextArrayToRawValues(options);
+ values = optionsParseRawValues(values, relopts->spec_set, validate);
+ opts = optionsValuesToBytea(values, relopts->spec_set);
+
+ /*
+ * Kind of ugly conversion here for backward compatibility. Would be
+ * removed while moving opclass options to options.c API
+ */
- foreach(lc, relopts->options)
+ if (validate && relopts->validators)
{
- local_relopt *opt = lfirst(lc);
+ int val_count = list_length(values);
+ int i;
+ option_value *val_array;
- elems[i].optname = opt->option->name;
- elems[i].opttype = opt->option->type;
- elems[i].offset = opt->offset;
+ val_array = palloc(sizeof(option_value) * val_count);
- i++;
- }
+ i = 0;
+ foreach(lc, values)
+ {
+ option_value *val = lfirst(lc);
- vals = parseLocalRelOptions(relopts, options, validate);
- opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
- fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
- elems, noptions);
+ memcpy(&(val_array[i]), val, sizeof(option_value));
+ i++;
+ }
- if (validate)
foreach(lc, relopts->validators)
- ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+ ((relopts_validator) lfirst(lc)) (opts, val_array, val_count);
- if (elems)
- pfree(elems);
+ pfree(val_array);
+ }
return opts;
}
/*
- * Option parser for partitioned tables
- */
-bytea *
-partitioned_table_reloptions(Datum reloptions, bool validate)
-{
- if (validate && reloptions)
- ereport(ERROR,
- errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot specify storage parameters for a partitioned table"),
- errhint("Specify storage parameters for its leaf partitions instead."));
- return NULL;
-}
-
-/*
- * Option parser for views
+ * get_view_relopt_spec_set
+ * Returns an options catalog for view relation.
*/
-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));
-}
+static options_spec_set *view_relopt_spec_set = NULL;
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_spec_set *
+get_view_relopt_spec_set(void)
{
- StdRdOptions *rdopts;
+ if (view_relopt_spec_set)
+ return view_relopt_spec_set;
- switch (relkind)
- {
- 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;
- }
-}
+ view_relopt_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(ViewOptions), false, 3);
+ optionsSpecSetAddBool(view_relopt_spec_set, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ offsetof(ViewOptions, security_barrier), NULL, false);
-/*
- * 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);
+ 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);
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ 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 amoptions(reloptions, validate);
+ return view_relopt_spec_set;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_spec_set
+ * Returns an Options Spec Set for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+static options_spec_set *attribute_options_spec_set = NULL;
+
+options_spec_set *
+get_attribute_options_spec_set(void)
{
- 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));
+ if (attribute_options_spec_set)
+ return attribute_options_spec_set;
+
+ attribute_options_spec_set = allocateOptionsSpecSet(NULL,
+ sizeof(AttributeOpts), false, 2);
+
+ 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);
+
+ 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 attribute_options_spec_set;
}
+
/*
- * Option parser for tablespace reloptions
- */
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
+ * get_tablespace_options_spec_set
+ * Returns an Options Spec Set for tablespaces
+*/
+static options_spec_set *tablespace_options_spec_set = NULL;
+
+options_spec_set *
+get_tablespace_options_spec_set(void)
{
- 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));
+ if (tablespace_options_spec_set)
+ return tablespace_options_spec_set;
+
+ 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);
+
+ 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);
+
+ 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;
}
/*
@@ -2128,33 +770,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.c
index 78f7b7a2495..6d4248ffe09 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/index_selfuncs.h"
#include "utils/rel.h"
#include "utils/typcache.h"
+#include "utils/guc.h"
/*
@@ -74,7 +75,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = ginbuildphasename;
amroutine->amvalidate = ginvalidate;
@@ -89,6 +89,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amreloptspecset = gingetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -603,21 +604,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
*
@@ -729,3 +715,29 @@ ginbuildphasename(int64 phasenum)
return NULL;
}
}
+
+static options_spec_set *gin_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 1840f04bfbf..611d4b523d0 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -95,7 +95,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = gistcanreturn;
amroutine->amcostestimate = gistcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = gistoptions;
amroutine->amproperty = gistproperty;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = gistvalidate;
@@ -112,6 +111,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = NULL;
amroutine->amtranslatecmptype = gisttranslatecmptype;
+ amroutine->amreloptspecset = gistgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index a6b701943d3..b578031207c 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 "common/pg_prng.h"
#include "storage/indexfsm.h"
#include "utils/float.h"
@@ -908,20 +908,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.
*
@@ -1109,3 +1095,39 @@ gisttranslatecmptype(CompareType cmptype, Oid opfamily)
result = OidFunctionCall1Coll(funcid, InvalidOid, Int32GetDatum(cmptype));
return DatumGetUInt16(result);
}
+
+/* values from GistOptBufferingMode */
+static 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;
+
+options_spec_set *
+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.c
index 53061c819fb..d83a26e39a3 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -94,7 +94,6 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = hashcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = hashoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = hashvalidate;
@@ -111,6 +110,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = hashtranslatestrategy;
amroutine->amtranslatecmptype = hashtranslatecmptype;
+ amroutine->amreloptspecset = hashgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
index 66c39f60654..8cc1e079200 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 "utils/lsyscache.h"
@@ -271,19 +271,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
*/
@@ -619,3 +606,22 @@ _hash_kill_items(IndexScanDesc scan)
else
_hash_relbuf(rel, buf);
}
+
+static options_spec_set *hash_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index c0a8833e068..5ccf2f9d5bc 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -21,6 +21,7 @@
#include "access/nbtree.h"
#include "access/relscan.h"
#include "access/stratnum.h"
+#include "access/options.h"
#include "commands/progress.h"
#include "commands/vacuum.h"
#include "nodes/execnodes.h"
@@ -137,7 +138,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = btcanreturn;
amroutine->amcostestimate = btcostestimate;
amroutine->amgettreeheight = btgettreeheight;
- amroutine->amoptions = btoptions;
amroutine->amproperty = btproperty;
amroutine->ambuildphasename = btbuildphasename;
amroutine->amvalidate = btvalidate;
@@ -154,6 +154,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = btparallelrescan;
amroutine->amtranslatestrategy = bttranslatestrategy;
amroutine->amtranslatecmptype = bttranslatecmptype;
+ amroutine->amreloptspecset = btgetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -1555,3 +1556,35 @@ bttranslatecmptype(CompareType cmptype, Oid opfamily)
return InvalidStrategy;
}
}
+
+static options_spec_set *bt_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index efe58beaaad..28a3c0be76b 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -18,7 +18,8 @@
#include <time.h>
#include "access/nbtree.h"
-#include "access/reloptions.h"
+#include "storage/lock.h"
+#include "access/relscan.h"
#include "commands/progress.h"
#include "miscadmin.h"
#include "utils/datum.h"
@@ -2715,23 +2716,6 @@ BTreeShmemInit(void)
Assert(found);
}
-bytea *
-btoptions(Datum reloptions, bool validate)
-{
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(BTOptions, fillfactor)},
- {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
- offsetof(BTOptions, vacuum_cleanup_index_scale_factor)},
- {"deduplicate_items", RELOPT_TYPE_BOOL,
- offsetof(BTOptions, deduplicate_items)}
- };
-
- return (bytea *) build_reloptions(reloptions, validate,
- RELOPT_KIND_BTREE,
- sizeof(BTOptions),
- tab, lengthof(tab));
-}
-
/*
* btproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 95fea74e296..b1c0a37311e 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"
@@ -80,7 +80,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = spgcanreturn;
amroutine->amcostestimate = spgcostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = spgoptions;
amroutine->amproperty = spgproperty;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = spgvalidate;
@@ -97,6 +96,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amparallelrescan = NULL;
amroutine->amtranslatestrategy = NULL;
amroutine->amtranslatecmptype = NULL;
+ amroutine->amreloptspecset = spggetreloptspecset;
PG_RETURN_POINTER(amroutine);
}
@@ -752,22 +752,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).
@@ -1366,3 +1350,24 @@ spgproperty(Oid index_oid, int attno,
return true;
}
+
+static options_spec_set *spgist_relopt_specset = NULL;
+
+options_spec_set *
+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.c
index 0a4155773eb..001f811fa73 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -87,6 +87,7 @@ create_ctas_internal(List *attrList, IntoClause *into)
Datum toast_options;
const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
ObjectAddress intoRelationAddr;
+ List *toastDefList;
/* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
is_matview = (into->viewQuery != NULL);
@@ -121,14 +122,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.c
index c14e038d54f..1c6426e6c57 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -112,7 +112,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 89cc83e8843..7f93edf986f 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -20,6 +20,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"
@@ -570,8 +571,8 @@ DefineIndex(Oid tableId,
IndexAmRoutine *amRoutine;
bool amcanorder;
bool amissummarizing;
- amoptions_function amoptions;
bool exclusion;
+ amreloptspecset_function amreloptspecsetfn;
bool partitioned;
bool safe_index;
Datum reloptions;
@@ -893,7 +894,7 @@ DefineIndex(Oid tableId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amreloptspecsetfn = amRoutine->amreloptspecset;
amissummarizing = amRoutine->amsummarizing;
pfree(amRoutine);
@@ -908,10 +909,18 @@ DefineIndex(Oid tableId,
/*
* 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.
@@ -2238,8 +2247,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
Assert(attn < nkeycols);
opclassOptions[attn] =
- transformRelOptions((Datum) 0, attribute->opclassopts,
- NULL, NULL, false, false);
+ optionsDefListToTextArray(attribute->opclassopts);
}
else
opclassOptions[attn] = (Datum) 0;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 129c97fdf28..d5e6cad5750 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -21,6 +21,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"
@@ -751,7 +752,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
ListCell *listptr;
AttrNumber attnum;
bool partitioned;
- const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
LOCKMODE parentLockmode;
@@ -900,19 +900,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);
- break;
+ {
+ /* If it is not all listed above, then it if heap */
+ const char *const 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 */
+ const char *const 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)
@@ -4524,7 +4541,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);
@@ -4566,7 +4583,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.
@@ -4787,9 +4804,14 @@ 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, AccessShareLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel, AccessShareLock);
+ break;
+ }
case AT_AttachPartition:
cmd_lockmode = ShareUpdateExclusiveLock;
break;
@@ -8946,12 +8968,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));
@@ -15923,7 +15946,9 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
Datum repl_val[Natts_pg_class];
bool repl_null[Natts_pg_class];
bool repl_repl[Natts_pg_class];
- const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
+ List *optionsDefList;
+ options_spec_set *optionsSpecSet;
+ const char *const heap_namespaces[] = HEAP_RELOPT_NAMESPACES;
if (defList == NIL && operation != AT_ReplaceRelOptions)
return; /* nothing to do */
@@ -15956,25 +15981,36 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
}
/* Generate new proposed reloptions (text array) */
- newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
- operation == AT_ResetRelOptions);
/* Validate */
+
+ optionsSpecSet = NULL;
switch (rel->rd_rel->relkind)
{
case RELKIND_RELATION:
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))));
+ optionsDefList = defList;
+ optionsSpecSet = rel->rd_indam->amreloptspecset();
break;
case RELKIND_TOASTVALUE:
/* fall through to error -- shouldn't ever get here */
@@ -15987,11 +16023,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;
@@ -16079,10 +16119,11 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
datum = (Datum) 0;
}
- newOptions = transformRelOptions(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.c
index a9005cc7212..5dc733905be 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -333,10 +333,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
@@ -1052,11 +1051,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.c
index f0835fc3070..87780f32309 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -80,7 +80,7 @@ GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
if (isnull)
fdw->options = NIL;
else
- fdw->options = untransformRelOptions(datum);
+ fdw->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -167,7 +167,7 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
if (isnull)
server->options = NIL;
else
- server->options = untransformRelOptions(datum);
+ server->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -239,7 +239,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);
+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -276,7 +276,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);
+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -309,7 +309,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);
+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -526,7 +526,7 @@ pg_options_to_table(PG_FUNCTION_ARGS)
List *options;
ReturnSetInfo *rsinfo;
- options = untransformRelOptions(array);
+ options = optionsTextArrayToDefList(array);
rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
/* prepare the result set */
@@ -624,7 +624,7 @@ is_conninfo_option(const char *option, Oid context)
Datum
postgresql_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index abbe1bb45a3..51c3fffe3e8 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1925,7 +1925,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;
@@ -1989,7 +1989,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 25fe3d58016..56cc463ee0e 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1159,6 +1159,7 @@ ProcessUtilitySlow(ParseState *pstate,
CreateStmt *cstmt = (CreateStmt *) stmt;
Datum toast_options;
const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
+ List *toastDefList;
/* Remember transformed RangeVar for LIKE */
table_rv = cstmt->relation;
@@ -1182,15 +1183,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);
@@ -1299,9 +1301,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, AccessShareLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
AlterTableUtilityContext atcontext;
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 5c8360c08b5..f2850d5fc38 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"
@@ -176,7 +177,8 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 9f54a9e72b7..e71fc1be19b 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -468,7 +468,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
- amoptions_function amoptsfn;
+ amreloptspecset_function amoptspecsetfn;
relation->rd_options = NULL;
@@ -483,11 +483,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;
@@ -498,7 +498,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
* we might not have any other for pg_class yet (consider executing this
* code for pg_class itself)
*/
- options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+ options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptspecsetfn);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index 23458599298..3010692c442 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,8 @@ get_tablespace(Oid spcid)
opts = NULL;
else
{
- bytea *bytea_opts = tablespace_reloptions(datum, false);
+ bytea *bytea_opts = optionsTextArrayToBytea(
+ get_tablespace_options_spec_set(), datum, 0);
opts = MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts));
memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index c4a0737731f..1d8c28b7c8e 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -15,6 +15,7 @@
#include "access/cmptype.h"
#include "access/genam.h"
#include "access/stratnum.h"
+#include "access/options.h"
/*
* We don't wish to include planner header files here, since most of an index
@@ -159,9 +160,9 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
*/
typedef int (*amgettreeheight_function) (Relation rel);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
+
+/* get Spec Set for relation options */
+typedef options_spec_set *(*amreloptspecset_function) ();
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
@@ -298,7 +299,7 @@ typedef struct IndexAmRoutine
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
amgettreeheight_function amgettreeheight; /* can be NULL */
- amoptions_function amoptions;
+ amreloptspecset_function amreloptspecset; /* can be NULL */
amproperty_function amproperty; /* can be NULL */
ambuildphasename_function ambuildphasename; /* can be NULL */
amvalidate_function amvalidate;
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 821f1e02806..88434a55612 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -37,6 +37,8 @@ typedef struct BrinStatsData
#define BRIN_DEFAULT_PAGES_PER_RANGE 128
+#define BRIN_MIN_PAGES_PER_RANGE 1
+#define BRIN_MAX_PAGES_PER_RANGE 131072
#define BrinGetPagesPerRange(relation) \
(AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
relation->rd_rel->relam == BRIN_AM_OID), \
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index d093a0bf130..2513250fe6a 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -109,6 +109,7 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *brinoptions(Datum reloptions, bool validate);
+extern options_spec_set *bringetreloptspecset(void);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index aee1f70c22e..6af01feab3b 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -110,6 +110,7 @@ extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
extern char *ginbuildphasename(int64 phasenum);
+extern options_spec_set *gingetreloptspecset(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 39404ec7cdb..7ed2230e0be 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
*/
@@ -479,7 +481,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
#define GIST_MIN_FILLFACTOR 10
#define GIST_DEFAULT_FILLFACTOR 90
-extern bytea *gistoptions(Datum reloptions, bool validate);
+extern options_spec_set *gistgetreloptspecset(void);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 073ad29b19b..3dccf6ed279 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -380,7 +380,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,
@@ -477,6 +476,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 options_spec_set *hashgetreloptspecset(void);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 0c43767f8c3..2f1de1041c1 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1307,7 +1307,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 options_spec_set *btgetreloptspecset(void);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 00000000000..c1df04c90f4
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,274 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2025, 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;
+
+/*
+ * Each Option Value item passes through certain life cycle. Option Value
+ * Status specifies at what point of life cycle this Option Value is now.
+ * Option Value Statuses are:
+ * EMPTY - Option Value structure have been just created
+ * RAW - Option Value have been read, but has not been parsed or validated yet.
+ * Use raw_value structure member to access Raw Value
+ * PARSED - Option Value have been parsed, you should use proper union member
+ * to access Parsed Option Value, according to Option Type
+ * FOR_RESET - Specifies that this Option is designated for resetting to
+ * default value
+ */
+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 Option
+ * Spec Set 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 assert_on_realloc; /* If number of items of the spec_set were
+ * strictly set to certain value assert on
+ * adding more items */
+ 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 *namspace; /* 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 *namspace;
+ 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 *namspace,
+ 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,
+ const char * const* allowed_namspaces);
+extern List *optionsDefListFilterNamespaces(List *defList,
+ const char *namspace);
+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.h
index 43445cdcc6c..b42bb614e03 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-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -25,175 +20,36 @@
#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 */
+ 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 *relopts, Size relopt_struct_size);
extern void register_reloptions_validator(local_relopts *relopts,
@@ -210,7 +66,7 @@ extern void add_local_real_reloption(local_relopts *relopts, 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 *relopts, const char *name,
@@ -219,29 +75,17 @@ extern void add_local_string_reloption(local_relopts *relopts, const char *name,
validate_string_relopt validator,
fill_string_relopt filler, int offset);
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const char *namspace, const char *const validnsps[],
- bool acceptOidsOff, bool isReset);
-extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern void *build_reloptions(Datum reloptions, bool validate,
- relopt_kind kind,
- Size relopt_struct_size,
- const relopt_parse_elt *relopt_elems,
- int num_relopt_elems);
+ amreloptspecset_function amoptions_def_set);
extern void *build_local_reloptions(local_relopts *relopts, Datum options,
bool validate);
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+options_spec_set *get_heap_relopt_spec_set(void);
+options_spec_set *get_toast_relopt_spec_set(void);
+options_spec_set *get_partitioned_relopt_spec_set(void);
+options_spec_set *get_view_relopt_spec_set(void);
+options_spec_set *get_attribute_options_spec_set(void);
+options_spec_set *get_tablespace_options_spec_set(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index cbe9b347d8f..c92c8698b98 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -189,9 +189,6 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
-/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
-
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index cb43a278f46..9f78a726646 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -530,6 +530,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 options_spec_set *spggetreloptspecset(void);
/* spgdoinsert.c */
extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 6832470d387..03a656cb4a8 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -36,7 +36,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
struct AlterTableUtilityContext *context);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
index 94ef639b6fc..5f6066a6654 100644
--- a/src/test/modules/dummy_index_am/dummy_index_am.c
+++ b/src/test/modules/dummy_index_am/dummy_index_am.c
@@ -14,18 +14,14 @@
#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"
PG_MODULE_MAGIC;
-/* parse table for fillRelOptions */
-static relopt_parse_elt di_relopt_tab[6];
-
-/* Kind of relation options for dummy index */
-static relopt_kind di_relopt_kind;
+void _PG_init(void);
typedef enum DummyAmEnum
{
@@ -45,7 +41,7 @@ typedef struct DummyIndexOptions
int option_string_null_offset;
} DummyIndexOptions;
-static relopt_enum_elt_def dummyAmEnumValues[] =
+static opt_enum_elt_def dummyAmEnumValues[] =
{
{"one", DUMMY_AM_ENUM_ONE},
{"two", DUMMY_AM_ENUM_TWO},
@@ -59,77 +55,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;
+options_spec_set *digetreloptspecset(void);
+
+options_spec_set *
+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.
*/
@@ -214,19 +215,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.
*/
@@ -310,7 +298,6 @@ dihandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = dicostestimate;
amroutine->amgettreeheight = NULL;
- amroutine->amoptions = dioptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = divalidate;
@@ -324,12 +311,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/modules/test_oat_hooks/expected/alter_table.out b/src/test/modules/test_oat_hooks/expected/alter_table.out
index 8cbacca2c9b..8319cd06941 100644
--- a/src/test/modules/test_oat_hooks/expected/alter_table.out
+++ b/src/test/modules/test_oat_hooks/expected/alter_table.out
@@ -82,6 +82,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -89,6 +91,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -96,6 +100,8 @@ ALTER TABLE test_oat_schema.test_oat_tab FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -103,6 +109,8 @@ ALTER TABLE test_oat_schema.test_oat_tab NO FORCE ROW LEVEL SECURITY;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in process utility: superuser finished ALTER TABLE
@@ -111,6 +119,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -120,6 +130,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE RULE test_oat_notify;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -130,6 +142,8 @@ ALTER TABLE test_oat_schema.test_oat_tab DISABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
@@ -139,6 +153,8 @@ ALTER TABLE test_oat_schema.test_oat_tab ENABLE TRIGGER test_oat_trigger;
NOTICE: in process utility: superuser attempting ALTER TABLE
NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [report on violation, allowed]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser finished alter (subId=0x0) [explicit without auxiliary object]
NOTICE: in object access: superuser attempting alter (subId=0x0) [explicit without auxiliary object]
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
index 9de19b4e3f1..67f0a828f9f 100644
--- a/src/test/regress/expected/reloptions.out
+++ b/src/test/regress/expected/reloptions.out
@@ -175,7 +175,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
@@ -194,6 +194,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.sql
index 24fbe0b478d..9b404295c05 100644
--- a/src/test/regress/sql/reloptions.sql
+++ b/src/test/regress/sql/reloptions.sql
@@ -112,6 +112,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
--
--
2.39.2