From: Nikolay Shaplov <dhyan@nataraj.su>
To: Alvaro Herrera <alvherre@alvh.no-ip.org>
Cc: pgsql-hackers@postgresql.org, Andres Freund <andres@anarazel.de>
Subject: Re: [PATCH] Move all am-related reloption code into src/backend/access/[am-name] and get rid of relopt_kind for custom AM
Date: Mon, 30 Oct 2017 12:49:56 +0300
Message-ID: <3148424.k4fWhFzS7G@x200m> (raw)
In-Reply-To: <20170903094543.kkqdbdjuxwa5z6ji@alvherre.pgsql>
References: <20170903094543.kkqdbdjuxwa5z6ji@alvherre.pgsql>
List-Unsubscribe: <mailto:majordomo@postgresql.org?body=unsub%20pgsql-hackers>
В письме от 3 сентября 2017 11:45:43 пользователь Alvaro Herrera написал:
Yet another git rebase. This patch can be applied to master branch again.
For this patch no review needed now, as I will offer part of it (one that
refuses to set toast reloptions when there is no TOAST table) as a separate
patch.
--
Do code for fun. Can do it for money (Perl & C/C++ ~10h/week)
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackersAttachments:reloptions8.diff (text/x-patch, 200.2K) downloaddiff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.hindex f3df1af..9c27326 100644--- a/contrib/bloom/bloom.h+++ b/contrib/bloom/bloom.h@@ -204,7 +204,6 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.cindex f2eda67..e937fed 100644--- a/contrib/bloom/blutils.c+++ b/contrib/bloom/blutils.c@@ -35,49 +35,13 @@
PG_FUNCTION_INFO_V1(blhandler);
-/* Kind of relation options for bloom index */-static relopt_kind bl_relopt_kind;--/* parse table for fillRelOptions */-static relopt_parse_elt bl_relopt_tab[INDEX_MAX_KEYS + 1];+/* Catalog of relation options for bloom index */+static options_catalog *bl_relopt_catalog;
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);- 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);- 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;- }-}+static void *blrelopt_catalog(void);+static void blReloptionPostprocess(void *, bool validate);
/*
* Construct a default set of Bloom options.
@@ -129,7 +93,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = blvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = blcostestimate;
- amroutine->amoptions = bloptions;+ amroutine->amrelopt_catalog = blrelopt_catalog;
amroutine->amproperty = NULL;
amroutine->amvalidate = blvalidate;
amroutine->ambeginscan = blbeginscan;
@@ -146,6 +110,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.
*/
@@ -466,24 +452,40 @@ BloomInitMetapage(Relation index)
UnlockReleaseBuffer(metaBuffer);
}
-/*- * Parse reloptions for bloom index, producing a BloomOptions struct.- */-bytea *-bloptions(Datum reloptions, bool validate)+static void *+blrelopt_catalog(void)
{
- relopt_value *options;- int numoptions;- BloomOptions *rdopts;+ if (!bl_relopt_catalog)+ {- /* Parse the user-given reloptions */- options = parseRelOptions(reloptions, validate, bl_relopt_kind, &numoptions);- rdopts = allocateReloptStruct(sizeof(BloomOptions), options, numoptions);- fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numoptions,- validate, bl_relopt_tab, lengthof(bl_relopt_tab));+ int i;+ char buf[16];- /* Convert signature length from # of bits to # to words, rounding up */- rdopts->bloomLength = (rdopts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;+ bl_relopt_catalog = allocateOptionsCatalog(NULL,+ sizeof(BloomOptions), INDEX_MAX_KEYS + 1);+ bl_relopt_catalog->postprocess_fun = blReloptionPostprocess;++ optionsCatalogAddItemInt(bl_relopt_catalog, "length",+ "Length of signature in bits",+ NoLock, /* No lock as far as ALTER is+ * forbidden */+ OPTION_DEFINITION_FLAG_FORBID_ALTER,+ offsetof(BloomOptions, bloomLength),+ DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH);- return (bytea *) rdopts;+ /* 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);+ optionsCatalogAddItemInt(bl_relopt_catalog, buf,+ "Number of bits for corresponding column",+ NoLock, /* No lock as far as ALTER is+ * forbidden */+ OPTION_DEFINITION_FLAG_FORBID_ALTER,+ offsetof(BloomOptions, bitSize[i]),+ DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS);+ }++ }+ return (void *) bl_relopt_catalog;
}
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.outindex 5ab9e34..732f8b5 100644--- a/contrib/bloom/expected/bloom.out+++ b/contrib/bloom/expected/bloom.out@@ -222,8 +222,27 @@ SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
(1 row)
-- check for min and max values
-\set VERBOSITY terse
CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
ERROR: value 0 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);+ERROR: value 4097 out of bounds for option "length"+DETAIL: Valid values are between "1" and "4096".
CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
ERROR: value 0 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);+ERROR: value 4096 out of bounds for option "col1"+DETAIL: Valid values are between "1" and "4095".+-- 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+-- check that ALTERing of reloptions is forbidden+ALTER INDEX bloomidx SET (length=4);+ERROR: changing parameter "length" is not allowed+ALTER INDEX bloomidx SET (col1=4);+ERROR: changing parameter "col1" is not allowed+ALTER INDEX bloomidx RESET (length);+ERROR: changing parameter "length" is not allowed+ALTER INDEX bloomidx RESET (col1);+ERROR: changing parameter "col1" is not alloweddiff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sqlindex 32755f2..f164e10 100644--- a/contrib/bloom/sql/bloom.sql+++ b/contrib/bloom/sql/bloom.sql@@ -89,6 +89,16 @@ DROP INDEX bloomidx;
CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1=4);
SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
-- check for min and max values
-\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 (length=4097);
CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);+-- check post_validate for colN<lengh+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);++-- check that ALTERing of reloptions is forbidden++ALTER INDEX bloomidx SET (length=4);+ALTER INDEX bloomidx SET (col1=4);+ALTER INDEX bloomidx RESET (length);+ALTER INDEX bloomidx RESET (col1);diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.cindex 7dc7716..c4a9e29 100644--- a/contrib/dblink/dblink.c+++ b/contrib/dblink/dblink.c@@ -1960,7 +1960,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.cindex 94e50e9..b38c15c 100644--- a/contrib/file_fdw/file_fdw.c+++ b/contrib/file_fdw/file_fdw.c@@ -193,7 +193,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.cindex 67e1c59..3fd44bc 100644--- a/contrib/postgres_fdw/option.c+++ b/contrib/postgres_fdw/option.c@@ -65,7 +65,7 @@ PG_FUNCTION_INFO_V1(postgres_fdw_validator);
Datum
postgres_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.cindex b3aa6d1..236b6c3 100644--- a/src/backend/access/brin/brin.c+++ b/src/backend/access/brin/brin.c@@ -19,7 +19,7 @@
#include "access/brin_page.h"
#include "access/brin_pageops.h"
#include "access/brin_xlog.h"
-#include "access/reloptions.h"+#include "access/options.h"
#include "access/relscan.h"
#include "access/xloginsert.h"
#include "catalog/index.h"
@@ -73,6 +73,7 @@ static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a,
BrinTuple *b);
static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy);
+static void *bringetreloptcatalog(void);
/*
* BRIN handler function: return IndexAmRoutine with access method parameters
@@ -106,7 +107,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = brinvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = brincostestimate;
- amroutine->amoptions = brinoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = brinvalidate;
amroutine->ambeginscan = brinbeginscan;
@@ -116,6 +116,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = brinendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = bringetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -800,37 +801,6 @@ brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
}
/*
- * reloptions processor for BRIN indexes- */-bytea *-brinoptions(Datum reloptions, bool validate)-{- relopt_value *options;- BrinOptions *rdopts;- int numoptions;- static const relopt_parse_elt tab[] = {- {"pages_per_range", RELOPT_TYPE_INT, offsetof(BrinOptions, pagesPerRange)},- {"autosummarize", RELOPT_TYPE_BOOL, offsetof(BrinOptions, autosummarize)}- };-- options = parseRelOptions(reloptions, validate, RELOPT_KIND_BRIN,- &numoptions);-- /* if none set, we're done */- if (numoptions == 0)- return NULL;-- rdopts = allocateReloptStruct(sizeof(BrinOptions), options, numoptions);-- fillRelOptions((void *) rdopts, sizeof(BrinOptions), options, numoptions,- validate, tab, lengthof(tab));-- pfree(options);-- return (bytea *) rdopts;-}--/*
* SQL-callable function to scan through an index and summarize all ranges
* that are not currently summarized.
*/
@@ -1428,3 +1398,33 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy)
if (vacuum_fsm)
FreeSpaceMapVacuum(idxrel);
}
++static options_catalog *brin_relopt_catalog = NULL;++static void *+bringetreloptcatalog(void)+{+ if (!brin_relopt_catalog)+ {+ brin_relopt_catalog = allocateOptionsCatalog(NULL,+ sizeof(BrinOptions), 2);++ optionsCatalogAddItemInt(brin_relopt_catalog, "pages_per_range",+ "Number of pages that each page range covers in a BRIN index",+ NoLock, /* since ALTER is not allowed+ * no lock needed */+ OPTION_DEFINITION_FLAG_FORBID_ALTER,+ offsetof(BrinOptions, pagesPerRange),+ BRIN_DEFAULT_PAGES_PER_RANGE,+ BRIN_MIN_PAGES_PER_RANGE,+ BRIN_MAX_PAGES_PER_RANGE);++ optionsCatalogAddItemBool(brin_relopt_catalog, "autosummarize",+ "Enables automatic summarization on this BRIN index",+ AccessExclusiveLock,+ 0,+ offsetof(BrinOptions, autosummarize),+ false);+ }+ return brin_relopt_catalog;+}diff --git a/src/backend/access/common/Makefile b/src/backend/access/common/Makefileindex f130b6e..ef305e6 100644--- a/src/backend/access/common/Makefile+++ b/src/backend/access/common/Makefile@@ -12,7 +12,7 @@ subdir = src/backend/access/common
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = bufmask.o heaptuple.o indextuple.o printsimple.o printtup.o \+OBJS = bufmask.o heaptuple.o indextuple.o options.o printsimple.o printtup.o \
reloptions.o scankey.o session.o tupconvert.o tupdesc.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.cnew file mode 100644index 0000000..4250173--- /dev/null+++ b/src/backend/access/common/options.c@@ -0,0 +1,1445 @@+/*-------------------------------------------------------------------------+ *+ * options.c+ * Core support for options used for relotions (pg_class.reloptions),+ * attoptions (pg_attribute.attoptions), and can be used for other+ * kinds of options.+ *+ * Here you can find functions that allow to define available options+ * for certain object (certain retaion type, attribute type etc), and+ * functions that allows to convert options from one represenation to+ * another and validate them.+ *+ * Portions Copyright (c) 1996-2017, 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"++/*+ * Any object (i.e. relation type) that want to use options, should create an+ * options catalog using allocateOptionsCatalog function, fill it with+ * optionsCatalogAddItemXxxx functions with definition of available options.+ * Then instance of this object can take option values from syntax analyzer+ * when instance in created using SQL command or from attribute of+ * pg_catalog.yyyy table, when object that already exists is loaded form+ * pg_catalog.+ *+ * Now options are mainly used for all kind or relations (pg_class.reloption)+ * and also for attributes (pg_attribute.attoptions) and tablespaces+ * (pg_tablespace.spcoptions)+ *+ * Options catalog for index relation options should be available from+ * amrelopt_catalog access method function. For non-index relations and for+ * other objects catalogs are available via functions from this file. E.g.+ * get_heap_relopt_catalog for heap relation options catalog.+ *+ * This file has two sections:+ *+ * 1. Functions for managing option catalogs+ *+ * 2. Functions for manipulating and transforming options+ *+ * More explanations are available at the beginning of each section+ */+++static option_definition_basic *allocateOptionDefinition(int type, char *name,+ char *desc, LOCKMODE lockmode,+ option_definition_flags flags, int struct_offset);++static void parse_one_option(option_value * option, char *text_str,+ int text_len, bool validate);+static void *optionsAllocateBytea(options_catalog * catalog, List *options);+++static List *+optionsDefListToRawValues(List *defList, options_parse_mode+ parse_mode);+static Datum optionsValuesToTextArray(List *options_values);+static List *optionsTextArrayToRawValues(Datum array_datum);+static List *optionsMergeOptionValues(List *old_options, List *new_options);+static List *optionsParseRawValues(List *raw_values, options_catalog * catalog,+ options_parse_mode mode);+static bytea *optionsValuesToBytea(List *options, options_catalog * catalog);+++/*+ * Options catalog functions+ */++/*+ * Options catalog describes options available for certain object. Catalog has+ * all necessary information for parsing transforming and validating options+ * for an object. All parsing/validation/transformation functions should not+ * know any details of option implementation for certain object, all this+ * information should be stored in catalog instead and interpreted by+ * pars/valid/transf functions blindly.+ *+ * The heart of the option catalog is an array of option definitions. Options+ * definition specifies name of option, type, range of acceptable values, and+ * default value.+ *+ * Options values can be one of the following types: bool, int, real, enum,+ * string. For more info see "option_type" and "optionsCatalogAddItemYyyy"+ * functions.+ *+ * Option definition flags allows to define parser behavior for special (or not+ * so special) cases. See option_definition_flags for more info.+ *+ * Options and Lock levels:+ *+ * The default choice for any new option should be AccessExclusiveLock.+ * In some cases the lock level can be reduced from there, but the lock+ * level chosen should always conflict with itself to ensure that multiple+ * changes aren't lost when we attempt concurrent changes.+ * The choice of lock level depends completely upon how that parameter+ * is used within the server, not upon how and when you'd like to change it.+ * Safety first. Existing choices are documented here, and elsewhere in+ * backend code where the parameters are used.+ *+ * In general, anything that affects the results obtained from a SELECT must be+ * protected by AccessExclusiveLock.+ *+ * Autovacuum related parameters can be set at ShareUpdateExclusiveLock+ * since they are only used by the AV procs and don't change anything+ * currently executing.+ *+ * Fillfactor can be set because it applies only to subsequent changes made to+ * data blocks, as documented in heapio.c+ *+ * n_distinct options can be set at ShareUpdateExclusiveLock because they+ * are only used during ANALYZE, which uses a ShareUpdateExclusiveLock,+ * so the ANALYZE will not be affected by in-flight changes. Changing those+ * values has no affect until the next ANALYZE, so no need for stronger lock.+ *+ * Planner-related parameters can be set with ShareUpdateExclusiveLock because+ * they only affect planning and not the correctness of the execution. Plans+ * cannot be changed in mid-flight, so changes here could not easily result in+ * new improved plans in any case. So we allow existing queries to continue+ * and existing plans to survive, a small price to pay for allowing better+ * plans to be introduced concurrently without interfering with users.+ *+ * Setting parallel_workers is safe, since it acts the same as+ * max_parallel_workers_per_gather which is a USERSET parameter that doesn't+ * affect existing plans or queries.+*/++/*+ * allocateOptionsCatalog+ * Allocate memory for a new catalog and initializes structure members.+ *+ * namespace - name of a namespace in which all items of the catalog+ * exists (E.g. namespace.option=value). For now postgres uses only toast.+ * namespace for tables.+ *+ * size_of_bytea - size of bytea-packed C-structure where all option values+ * would be finally mapped, so it can be fast and easily used by pg core+ *+ * num_items_expected - expected number of items in options catalog. If it is+ * set to positive value, proper chunk of memory would be allocated, and postgres+ * will assert if you try to add more options to the catalog. If it is set to -1, then+ * you can add any number of options to catalog, memory for entries would be dynamically+ * reallocated while adding. This feature was left for backward compatibility and is+ * not encouraged for production usage.+ */++options_catalog *+allocateOptionsCatalog(char *namespace, int size_of_bytea, int num_items_expected)+{+ MemoryContext oldcxt;+ options_catalog *catalog;++ oldcxt = MemoryContextSwitchTo(TopMemoryContext);+ catalog = palloc(sizeof(options_catalog));+ if (namespace)+ {+ catalog->namespace = palloc(strlen(namespace) + 1);+ strcpy(catalog->namespace, namespace);+ }+ else+ catalog->namespace = NULL;+ if (num_items_expected > 0)+ {+ catalog->num_allocated = num_items_expected;+ catalog->forbid_realloc = true;+ catalog->definitions = palloc(+ catalog->num_allocated * sizeof(option_definition_basic *));+ }+ else+ {+ catalog->num_allocated = 0;+ catalog->forbid_realloc = false;+ catalog->definitions = NULL;+ }+ catalog->num = 0;+ catalog->struct_size = size_of_bytea;+ catalog->postprocess_fun = NULL;+ MemoryContextSwitchTo(oldcxt);+ return catalog;+}++/*+ * optionCatalogAddItem+ * Add an already-created option definition to the catalog+ */+static void+optionCatalogAddItem(option_definition_basic * newoption,+ options_catalog * catalog)+{+ if (catalog->num >= catalog->num_allocated)+ {+ MemoryContext oldcxt;++ Assert(!catalog->forbid_realloc);+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);++ if (catalog->num_allocated == 0)+ {+ catalog->num_allocated = 8;+ catalog->definitions = palloc(+ catalog->num_allocated * sizeof(option_definition_basic *));+ }+ else+ {+ catalog->num_allocated *= 2;+ catalog->definitions = repalloc(catalog->definitions,+ catalog->num_allocated * sizeof(option_definition_basic *));+ }+ MemoryContextSwitchTo(oldcxt);+ }+ catalog->definitions[catalog->num] = newoption;+ catalog->num++;+}++/*+ * allocateOptionDefinition+ * Allocate a new option definition and initialize the type-agnostic+ * fields+ */+static option_definition_basic *+allocateOptionDefinition(int type, char *name, char *desc, LOCKMODE lockmode,+ option_definition_flags flags, int struct_offset)+{+ MemoryContext oldcxt;+ size_t size;+ option_definition_basic *newoption;++ oldcxt = MemoryContextSwitchTo(TopMemoryContext);++ switch (type)+ {+ case OPTION_TYPE_BOOL:+ size = sizeof(option_definition_bool);+ break;+ case OPTION_TYPE_INT:+ size = sizeof(option_definition_int);+ break;+ case OPTION_TYPE_REAL:+ size = sizeof(option_definition_real);+ break;+ case OPTION_TYPE_ENUM:+ size = sizeof(option_definition_enum);+ break;+ case OPTION_TYPE_STRING:+ size = sizeof(option_definition_string);+ break;+ default:+ elog(ERROR, "unsupported reloption type %d", type);+ return NULL; /* keep compiler quiet */+ }++ newoption = palloc(size);++ newoption->name = pstrdup(name);+ if (desc)+ newoption->desc = pstrdup(desc);+ else+ newoption->desc = NULL;+ newoption->type = type;+ newoption->lockmode = lockmode;+ newoption->flags = flags;+ newoption->struct_offset = struct_offset;++ MemoryContextSwitchTo(oldcxt);++ return newoption;+}++/*+ * optionsCatalogAddItemBool+ * Add a new boolean option definition to the catalog+ */+void+optionsCatalogAddItemBool(options_catalog * catalog, char *name, char *desc,+ LOCKMODE lockmode, option_definition_flags flags,+ int struct_offset, bool default_val)+{+ option_definition_bool *catalog_item;++ catalog_item = (option_definition_bool *)+ allocateOptionDefinition(OPTION_TYPE_BOOL, name, desc, lockmode,+ flags, struct_offset);++ catalog_item->default_val = default_val;++ optionCatalogAddItem((option_definition_basic *) catalog_item, catalog);+}++/*+ * optionsCatalogAddItemInt+ * Add a new integer option definition to the catalog+ */+void+optionsCatalogAddItemInt(options_catalog * catalog, char *name,+ char *desc, LOCKMODE lockmode, option_definition_flags flags,+ int struct_offset, int default_val, int min_val, int max_val)+{+ option_definition_int *catalog_item;++ catalog_item = (option_definition_int *)+ allocateOptionDefinition(OPTION_TYPE_INT, name, desc, lockmode,+ flags, struct_offset);++ catalog_item->default_val = default_val;+ catalog_item->min = min_val;+ catalog_item->max = max_val;++ optionCatalogAddItem((option_definition_basic *) catalog_item, catalog);+}++/*+ * optionsCatalogAddItemReal+ * Add a new float option to the catalog+ */+void+optionsCatalogAddItemReal(options_catalog * catalog, char *name, char *desc,+ LOCKMODE lockmode, option_definition_flags flags, int struct_offset,+ double default_val, double min_val, double max_val)+{+ option_definition_real *catalog_item;++ catalog_item = (option_definition_real *)+ allocateOptionDefinition(OPTION_TYPE_REAL, name, desc, lockmode,+ flags, struct_offset);++ catalog_item->default_val = default_val;+ catalog_item->min = min_val;+ catalog_item->max = max_val;++ optionCatalogAddItem((option_definition_basic *) catalog_item, catalog);+}++/*+ * optionsCatalogAddItemEnum+ * Add a new enum option to the catalog+ *+ * "allowed_values" is a pointer to a NULL-terminated char* array (last item of+ * an array should be null). This array contains a list of acceptable values+ * for the option.+ *+ * "default_val" is a number of item in allowed_values array that should be+ * set as default value. If you want to handle "option was not set" special+ * case, you can set default_val to -1+ */+void+optionsCatalogAddItemEnum(options_catalog * catalog, char *name, char *desc,+ LOCKMODE lockmode, option_definition_flags flags, int struct_offset,+ const char **allowed_values, int default_val)+{+ option_definition_enum *catalog_item;++ catalog_item = (option_definition_enum *)+ allocateOptionDefinition(OPTION_TYPE_ENUM, name, desc, lockmode,+ flags, struct_offset);++ catalog_item->default_val = default_val;+ catalog_item->allowed_values = allowed_values;++ optionCatalogAddItem((option_definition_basic *) catalog_item, catalog);+}++/*+ * optionsCatalogAddItemString+ * Add a new string option definition to the catalog+ *+ * "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+optionsCatalogAddItemString(options_catalog * catalog, char *name, char *desc,+ LOCKMODE lockmode, option_definition_flags flags, int struct_offset,+ char *default_val, validate_string_relopt validator)+{+ option_definition_string *catalog_item;++ /* make sure the validator/default combination is sane */+ if (validator)+ (validator) (default_val);++ catalog_item = (option_definition_string *)+ allocateOptionDefinition(OPTION_TYPE_STRING, name, desc, lockmode,+ flags, struct_offset);+ catalog_item->validate_cb = validator;++ if (default_val)+ catalog_item->default_val = MemoryContextStrdup(TopMemoryContext,+ default_val);+ else+ catalog_item->default_val = NULL;+ optionCatalogAddItem((option_definition_basic *) catalog_item, catalog);+}+++/*+ * Options transform functions+ */++/*+ * Option values exists in five representations: DefList, TextArray, Values and+ * Bytea:+ *+ * DefList: Is a List of DefElem structures, that comes from syntax analyzer.+ * It can be transformed to Values representation for further parsing and+ * validating+ *+ * Values: A List of option_value structures. Is divided into two subclasses:+ * RawValues, when values are already transformed from DefList or TextArray,+ * but not parsed yet. (In this case you should use raw_name and raw_value+ * structure members to see option content). ParsedValues (or just simple+ * Values) is crated after finding a definition for this option in a catalog+ * and after parsing of the raw value. For ParsedValues content is stored in+ * values structure member, and name can be taken from option definition in gen+ * structure member. Actually Value list can have both Raw and Parsed values,+ * as we do not validate options that came from database, and db option that+ * does not exist in catalog is just ignored, and kept as RawValues+ *+ * TextArray: The representation in which options for existing object comes+ * and goes from/to database; for example from pg_class.reloptions. It is a+ * plain TEXT[] db object with name=value text inside. This representation can+ * be transformed into Values for further processing, using options catalog.+ *+ * Bytea: Is a binary representation of options. Each object that has code that+ * uses options, should create a C-structure for this options, with varlen+ * 4-byte header in front of the data; all items of options catalog should have+ * an offset of a corresponding binary data in this structure, so transform+ * function can put this data in the correct place. One can transform options+ * data from values representation into Bytea, using catalog data, and then use+ * it as a usual Datum object, when needed. This Datum should be cached+ * somewhere (for example in rel->rd_options for relations) when object that+ * has option is loaded from db.+ */+++/* optionsDefListToRawValues+ * Converts option values that came from syntax analyzer (DefList) into+ * Values List.+ *+ * No parsing is done here except for checking that RESET syntax is correct+ * (syntax analyzer do not see difference between SET and RESET cases, we+ * should treat it here manually+ */+static List *+optionsDefListToRawValues(List *defList, options_parse_mode parse_mode)+{+ ListCell *cell;+ List *result = NIL;++ foreach(cell, defList)+ {+ option_value *option_dst;+ DefElem *def = (DefElem *) lfirst(cell);+ char *value;++ option_dst = palloc(sizeof(option_value));++ if (def->defnamespace)+ {+ option_dst->namespace = palloc(strlen(def->defnamespace) + 1);+ strcpy(option_dst->namespace, def->defnamespace);+ }+ else+ {+ option_dst->namespace = NULL;+ }+ option_dst->raw_name = palloc(strlen(def->defname) + 1);+ strcpy(option_dst->raw_name, def->defname);++ if (parse_mode & OPTIONS_PARSE_MODE_FOR_RESET)+ {+ /*+ * If this option came from RESET statement we should throw error+ * it it brings us name=value data, as syntax analyzer do not+ * prevent it+ */+ if (def->arg != NULL)+ ereport(ERROR,+ (errcode(ERRCODE_SYNTAX_ERROR),+ errmsg("RESET must not include values for parameters")));++ option_dst->status = OPTION_VALUE_STATUS_FOR_RESET;+ }+ else+ {+ /*+ * For SET statement we should treat (name) expression as if it is+ * actually (name=true) so do it here manually. In other cases+ * just use value as we should use it+ */+ option_dst->status = OPTION_VALUE_STATUS_RAW;+ if (def->arg != NULL)+ value = defGetString(def);+ else+ value = "true";+ option_dst->raw_value = palloc(strlen(value) + 1);+ strcpy(option_dst->raw_value, value);+ }++ result = lappend(result, option_dst);+ }+ return result;+}++/*+ * optionsValuesToTextArray+ * Converts List of option_values into TextArray+ *+ * Convertation is made to put options into database (e.g. in+ * pg_class.reloptions for all relation options)+ */++Datum+optionsValuesToTextArray(List *options_values)+{+ ArrayBuildState *astate = NULL;+ ListCell *cell;+ Datum result;++ foreach(cell, options_values)+ {+ option_value *option = (option_value *) lfirst(cell);+ const char *name;+ char *value;+ text *t;+ int len;++ /*+ * Raw value were not cleared while parsing, so instead of converting+ * it back, just use it to store value as text+ */+ value = option->raw_value;++ Assert(option->status != OPTION_VALUE_STATUS_EMPTY);++ /*+ * Name will be taken from option definition, if option were parsed or+ * from raw_name if option were not parsed for some reason+ */+ if (option->status == OPTION_VALUE_STATUS_PARSED)+ name = option->gen->name;+ else+ name = option->raw_name;++ /*+ * Now build "name=value" string and append it to the array+ */+ len = VARHDRSZ + strlen(name) + strlen(value) + 1;+ t = (text *) palloc(len + 1);+ SET_VARSIZE(t, len);+ sprintf(VARDATA(t), "%s=%s", name, value);+ astate = accumArrayResult(astate, PointerGetDatum(t), false,+ TEXTOID, CurrentMemoryContext);+ }+ if (astate)+ result = makeArrayResult(astate, CurrentMemoryContext);+ else+ result = (Datum) 0;++ return result;+}++/*+ * optionsTextArrayToRawValues+ * Converts options from TextArray format into RawValues list.+ *+ * This function is used to convert options data that comes from database to+ * List of option_values, for further parsing, and, in the case of ALTER+ * command, for merging with new option values.+ */+static 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;++ result = lappend(result, option_dst);+ }+ }+ return result;+}++/*+ * optionsMergeOptionValues+ * Merges two lists of option_values into one list+ *+ * This function is used to merge two Values list into one. It is used for all+ * kinds of ALTER commands when existing options are merged|replaced with new+ * options list. This function also process RESET variant of ALTER command. It+ * merges two lists as usual, and then removes all items with RESET flag on.+ *+ * Both incoming lists will be destroyed while merging+ */+static List *+optionsMergeOptionValues(List *old_options, List *new_options)+{+ List *result = NIL;+ ListCell *old_cell;+ ListCell *old_prev;+ ListCell *old_next;+ ListCell *new_cell;+ ListCell *new_prev;+ ListCell *new_next;++ old_prev = NULL;++ /*+ * First try to remove from old options list all values that exists in a+ * new options list+ */+ for (old_cell = list_head(old_options); old_cell; old_cell = old_next)+ {+ bool found;+ const char *old_name;+ option_value *old_option;++ old_next = lnext(old_cell);+ 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;++ /*+ * Try to find old_name option among new option+ */+ 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 (pg_strcasecmp(new_name, old_name) == 0)+ {+ found = true;+ break;+ }+ }++ /*+ * If found, delete old option from the list+ */+ if (found)+ {+ old_options = list_delete_cell(old_options, old_cell, old_prev);+ }+ else+ {+ old_prev = old_cell;+ }+ }++ /*+ * Remove from new_options all options that are for RESET. In old list all+ * this options were already removed in previous block, so all RESET+ * options are cleaned now+ */+ new_prev = NULL;+ for (new_cell = list_head(new_options); new_cell; new_cell = new_next)+ {+ option_value *new_option = (option_value *) lfirst(new_cell);++ new_next = lnext(new_cell);++ if (new_option->status == OPTION_VALUE_STATUS_FOR_RESET)+ new_options = list_delete_cell(new_options, new_cell, new_prev);+ else+ new_prev = new_cell;+ }++ /*+ * Now merge what remained of both lists+ */+ result = list_concat(old_options, new_options);+ return result;+}++/*+ * optionsDefListValdateNamespaces+ * Function checks that all options represented as DefList has no+ * namespaces or have namespaces only from allowed list+ *+ * Function accept options as DefList and NULL terminated list of allowed+ * namespaces. It throws an error if not proper namespace was found.+ *+ * This function actually used only for tables with it's toast. namespace+ */+void+optionsDefListValdateNamespaces(List *defList, char **allowed_namespaces)+{+ ListCell *cell;++ foreach(cell, defList)+ {+ DefElem *def = (DefElem *) lfirst(cell);++ /*+ * Checking namespace only for options that have namespaces. Options+ * with no namespaces are always accepted+ */+ if (def->defnamespace)+ {+ bool found = false;+ int i = 0;++ while (allowed_namespaces[i])+ {+ if (pg_strcasecmp(def->defnamespace,+ allowed_namespaces[i]) == 0)+ {+ found = true;+ break;+ }+ i++;+ }+ if (!found)+ ereport(ERROR,+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),+ errmsg("unrecognized parameter namespace \"%s\"",+ def->defnamespace)));+ }+ }+}++/*+ * optionsDefListFilterNamespaces+ * Iterates over DefList, choose items with specified namespace and adds+ * them to a result List+ *+ * This function does not destroy source DefList but does not create copies+ * of List nodes.+ * It is actually used only for tables, in order to split toast and heap+ * reloptions, so each one can be stored in on it's own pg_class record+ */+List *+optionsDefListFilterNamespaces(List *defList, char *namespace)+{+ ListCell *cell;+ List *result = NIL;++ foreach(cell, defList)+ {+ DefElem *def = (DefElem *) lfirst(cell);++ if ((!namespace && !def->defnamespace) ||+ (namespace && def->defnamespace &&+ pg_strcasecmp(namespace, def->defnamespace) == 0))+ {+ result = lappend(result, def);+ }+ }+ return result;+}++/*+ * optionsTextArrayToDefList+ * Convert the text-array format of reloptions into a List of DefElem.+ */+List *+optionsTextArrayToDefList(Datum options)+{+ List *result = NIL;+ ArrayType *array;+ Datum *optiondatums;+ int noptions;+ int i;++ /* Nothing to do if no options */+ if (!PointerIsValid(DatumGetPointer(options)))+ return result;++ array = DatumGetArrayTypeP(options);++ deconstruct_array(array, TEXTOID, -1, false, 'i',+ &optiondatums, NULL, &noptions);++ for (i = 0; i < noptions; i++)+ {+ char *s;+ char *p;+ Node *val = NULL;++ s = TextDatumGetCString(optiondatums[i]);+ p = strchr(s, '=');+ if (p)+ {+ *p++ = '\0';+ val = (Node *) makeString(pstrdup(p));+ }+ result = lappend(result, makeDefElem(pstrdup(s), val, -1));+ }++ return result;+}++/*+ * optionsParseRawValues+ * Parses and vlaidates (if proper flag is set) option_values. As a result+ * caller will get the list of parsed (or partly parsed) option_values+ *+ * This function is used in cases when caller gets raw values from db or+ * syntax and want to parse them.+ * This function uses option_catalog to get information about how each option+ * should be parsed.+ * If validate mode is off, function found an option that do not have proper+ * option_catalog entry, this option kept unparsed (if some garbage came from+ * the DB, we should put it back there)+ *+ * This function destroys incoming list.+ */+static List *+optionsParseRawValues(List *raw_values, options_catalog * catalog,+ options_parse_mode mode)+{+ ListCell *cell;+ List *result = NIL;+ bool *is_set;+ int i;+ bool validate = mode & OPTIONS_PARSE_MODE_VALIDATE;+ bool for_alter = mode & OPTIONS_PARSE_MODE_FOR_ALTER;+++ is_set = palloc0(sizeof(bool) * catalog->num);+ foreach(cell, raw_values)+ {+ option_value *option = (option_value *) lfirst(cell);+ bool found = false;+ bool skip = false;+++ if (option->status == OPTION_VALUE_STATUS_PARSED)+ {+ /*+ * This can happen while ALTER, when new values were already+ * parsed, but old values merged from DB are still raw+ */+ result = lappend(result, option);+ continue;+ }+ if (validate && option->namespace && (!catalog->namespace ||+ pg_strcasecmp(catalog->namespace, option->namespace) != 0))+ {+ ereport(ERROR,+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),+ errmsg("unrecognized parameter namespace \"%s\"",+ option->namespace)));+ }++ for (i = 0; i < catalog->num; i++)+ {+ option_definition_basic *definition = catalog->definitions[i];++ if (pg_strcasecmp(option->raw_name,+ definition->name) == 0)+ {+ /*+ * Skip option with "ignore" flag, as it is processed+ * somewhere else. (WITH OIDS special case)+ */+ if (definition->flags & OPTION_DEFINITION_FLAG_IGNORE)+ {+ found = true;+ skip = true;+ break;+ }++ /*+ * Reject option as if it was not in catalog. Needed for cases+ * when option should have default value, but should not be+ * changed+ */+ if (definition->flags & OPTION_DEFINITION_FLAG_REJECT)+ {+ found = false;+ break;+ }++ if (validate && is_set[i])+ {+ ereport(ERROR,+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),+ errmsg("parameter \"%s\" specified more than once",+ option->raw_name)));+ }+ if ((for_alter) &&+ (definition->flags & OPTION_DEFINITION_FLAG_FORBID_ALTER))+ {+ ereport(ERROR,+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),+ errmsg("changing parameter \"%s\" is not allowed",+ definition->name)));+ }+ if (option->status == OPTION_VALUE_STATUS_FOR_RESET)+ {+ /*+ * For RESET options do not need further processing so+ * mark it found and stop searching+ */+ found = true;+ break;+ }+ pfree(option->raw_name);+ option->raw_name = NULL;+ option->gen = definition;+ parse_one_option(option, NULL, -1, validate);+ is_set[i] = true;+ found = true;+ break;+ }+ }+ if (!found)+ {+ if (validate)+ {+ if (option->namespace)+ ereport(ERROR,+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),+ errmsg("unrecognized parameter \"%s.%s\"",+ option->namespace, option->raw_name)));+ else+ ereport(ERROR,+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),+ errmsg("unrecognized parameter \"%s\"",+ option->raw_name)));+ }++ /*+ * If we are parsing not in validate mode, then we should keep+ * unknown node, because non-validate mode is for data that is+ * already in the DB and should not be changed after altering+ * another entries+ */+ }+ if (!skip)+ result = lappend(result, option);+ }+ return result;+}++/*+ * parse_one_option+ *+ * Subroutine for optionsParseRawValues, to parse and validate a+ * single option's value+ */+static void+parse_one_option(option_value * option, char *text_str, int text_len,+ bool validate)+{+ char *value;+ bool parsed;++ value = option->raw_value;++ switch (option->gen->type)+ {+ case OPTION_TYPE_BOOL:+ {+ parsed = parse_bool(value, &option->values.bool_val);+ if (validate && !parsed)+ ereport(ERROR,+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),+ errmsg("invalid value for boolean option \"%s\": %s",+ option->gen->name, value)));+ }+ break;+ case OPTION_TYPE_INT:+ {+ option_definition_int *optint =+ (option_definition_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_definition_real *optreal =+ (option_definition_real *) option->gen;++ parsed = parse_real(value, &option->values.real_val);+ 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_definition_enum *opt_enum =+ (option_definition_enum *) option->gen;+ int i = 0;++ parsed = false;+ while (opt_enum->allowed_values[i])+ {+ if (pg_strcasecmp(value, opt_enum->allowed_values[i]) == 0)+ {+ option->values.enum_val = i;+ parsed = true;+ break;+ }+ i++;+ }+ if (!parsed)+ {+ int length = 0;+ char *str;+ char *ptr;++ /*+ * Generating list of allowed values: "value1", "value2",+ * ... "valueN"+ */+ i = 0;+ while (opt_enum->allowed_values[i])+ {+ length += strlen(opt_enum->allowed_values[i]) + 4;+ /* +4: two quotes, one comma, one space */+ i++;+ }++ /*+ * one byte not used for comma after the last item will be+ * used for \0; for another byte will do -1+ */+ str = palloc((length - 1) * sizeof(char));+ i = 0;+ ptr = str;+ while (opt_enum->allowed_values[i])+ {+ if (i != 0)+ {+ ptr[0] = ',';+ ptr[1] = ' ';+ ptr += 2;+ }+ ptr[0] = '"';+ ptr++;+ sprintf(ptr, "%s", opt_enum->allowed_values[i]);+ ptr += strlen(ptr);+ ptr[0] = '"';+ ptr++;+ i++;+ }+ *ptr = '\0';++ ereport(ERROR,+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),+ errmsg("invalid value for \"%s\" option",+ option->gen->name),+ errdetail("Valid values are %s.", str)));+ }+ }+ break;+ case OPTION_TYPE_STRING:+ {+ option_definition_string *optstring =+ (option_definition_string *) option->gen;++ option->values.string_val = value;+ if (validate && optstring->validate_cb)+ (optstring->validate_cb) (value);+ parsed = true;+ }+ break;+ default:+ elog(ERROR, "unsupported reloption type %d", option->gen->type);+ parsed = true; /* quiet compiler */+ break;+ }++ if (parsed)+ option->status = OPTION_VALUE_STATUS_PARSED;++}++/*+ * optionsAllocateBytea+ * Allocates memory for bytea options representation+ *+ * Function allocates memory for byrea structure of an option, plus adds space+ * for values of string options. We should keep all data including string+ * values in the same memory chunk, because Cache code copies bytea option+ * data from one MemoryConext to another without knowing about it's internal+ * structure, so it would not be able to copy string values if they are outside+ * of bytea memory chunk.+ */+static void *+optionsAllocateBytea(options_catalog * catalog, List *options)+{+ Size size;+ int i;+ ListCell *cell;+ int length;+ void *res;++ size = catalog->struct_size;++ /* Calculate size needed to store all string values for this option */+ for (i = 0; i < catalog->num; i++)+ {+ option_definition_basic *definition = catalog->definitions[i];+ bool found = false;+ option_value *option;++ /* Not interested in non-string options, skipping */+ if (definition->type != OPTION_TYPE_STRING)+ continue;++ /*+ * Trying to find option_value that references definition catalog+ * entry+ */+ foreach(cell, options)+ {+ option = (option_value *) lfirst(cell);+ if (option->status == OPTION_VALUE_STATUS_PARSED &&+ pg_strcasecmp(option->gen->name, definition->name) == 0)+ {+ found = true;+ break;+ }+ }+ if (found)+ /* If found, it'value will be stored */+ length = strlen(option->values.string_val) + 1;+ else+ /* If not found, then there would be default value there */+ if (((option_definition_string *) definition)->default_val)+ length = strlen(+ ((option_definition_string *) definition)->default_val) + 1;+ else+ length = 0;+ /* Add total length of all string values to basic size */+ size += length;+ }++ res = palloc0(size);+ SET_VARSIZE(res, size);+ return res;+}++/*+ * optionsValuesToBytea+ * Converts options from List of option_values to binary bytea structure+ *+ * Convertation goes according to options_catalog: each catalog item+ * has offset value, and option value in binary mode is written to the+ * structure with that offset.+ *+ * More special case is string values. Memory for bytea structure is allocated+ * by optionsAllocateBytea which adds some more space for string values to+ * the size of original structure. All string values are copied there and+ * inside the bytea structure an offset to that value is kept.+ *+ */+static bytea *+optionsValuesToBytea(List *options, options_catalog * catalog)+{+ char *data;+ char *string_values_buffer;+ int i;++ data = optionsAllocateBytea(catalog, options);++ /* place for string data starts right after original structure */+ string_values_buffer = data + catalog->struct_size;++ for (i = 0; i < catalog->num; i++)+ {+ option_value *found = NULL;+ ListCell *cell;+ char *item_pos;+ option_definition_basic *definition = catalog->definitions[i];++ if (definition->flags & OPTION_DEFINITION_FLAG_IGNORE)+ continue;++ /* Calculate the position of the item inside the structure */+ item_pos = data + definition->struct_offset;++ /* Looking for the corresponding option from options list */+ foreach(cell, options)+ {+ option_value *option = (option_value *) lfirst(cell);++ if (option->status == OPTION_VALUE_STATUS_RAW)+ continue; /* raw can come from db. Just ignore them then */+ Assert(option->status != OPTION_VALUE_STATUS_EMPTY);++ if (pg_strcasecmp(definition->name, option->gen->name) == 0)+ {+ found = option;+ break;+ }+ }+ /* writing to the proper position either option value or default val */+ switch (definition->type)+ {+ case OPTION_TYPE_BOOL:+ *(bool *) item_pos = found ?+ found->values.bool_val :+ ((option_definition_bool *) definition)->default_val;+ break;+ case OPTION_TYPE_INT:+ *(int *) item_pos = found ?+ found->values.int_val :+ ((option_definition_int *) definition)->default_val;+ break;+ case OPTION_TYPE_REAL:+ *(double *) item_pos = found ?+ found->values.real_val :+ ((option_definition_real *) definition)->default_val;+ break;+ case OPTION_TYPE_ENUM:+ *(int *) item_pos = found ?+ found->values.enum_val :+ ((option_definition_enum *) definition)->default_val;+ break;++ case OPTION_TYPE_STRING:+ {+ /*+ * For string options: writing string value at the string+ * buffer after the structure, and storing and offset to+ * that value+ */+ char *value = NULL;++ if (found)+ value = found->values.string_val;+ else+ value = ((option_definition_string *) definition)+ ->default_val;+ *(int *) item_pos = value ?+ string_values_buffer - data :+ OPTION_STRING_VALUE_NOT_SET_OFFSET;+ if (value)+ {+ strcpy(string_values_buffer, value);+ string_values_buffer += strlen(value) + 1;+ }+ }+ break;+ default:+ elog(ERROR, "unsupported reloption type %d",+ definition->type);+ break;+ }+ }+ return (void *) data;+}+++/*+ * transformOptions+ * This function is used by src/backend/commands/Xxxx in order to process+ * new option values, merge them with existing values (in the case of+ * ALTER command) and prepare to put them [back] into DB+ */++Datum+transformOptions(options_catalog * catalog, Datum oldOptions,+ List *defList, options_parse_mode parse_mode)+{+ Datum result;+ List *new_values;+ List *old_values;+ List *merged_values;++ /*+ * Parse and validate New values+ */+ new_values = optionsDefListToRawValues(defList, parse_mode);+ new_values = optionsParseRawValues(new_values, catalog,+ parse_mode | OPTIONS_PARSE_MODE_VALIDATE);++ /*+ * Old values exists in case of ALTER commands. Transform them to raw+ * values and merge them with new_values, and parse it.+ */+ if (PointerIsValid(DatumGetPointer(oldOptions)))+ {+ old_values = optionsTextArrayToRawValues(oldOptions);+ merged_values = optionsMergeOptionValues(old_values, new_values);++ /*+ * Parse options only after merging in order not to parse options that+ * would be removed by merging later+ */+ merged_values = optionsParseRawValues(merged_values, catalog, 0);+ }+ else+ {+ merged_values = new_values;+ }++ /*+ * If we have postprocess_fun function defined in catalog, 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 (catalog->postprocess_fun)+ {+ bytea *data = optionsValuesToBytea(merged_values, catalog);++ catalog->postprocess_fun(data, true);+ pfree(data);+ }++ /*+ * Convert options to TextArray format so caller can store them into+ * database+ */+ result = optionsValuesToTextArray(merged_values);+ return result;+}+++/*+ * optionsTextArrayToBytea+ * A meta-function that transforms options stored as TextArray into binary+ * (bytea) representation.+ *+ * This function runs other transform functions that leads to the desired+ * result in no-validation mode. This function is used by cache mechanism,+ * in order to load and cache options when object itself is loaded and cached+ */+bytea *+optionsTextArrayToBytea(options_catalog * catalog, Datum data)+{+ List *values;+ bytea *options;++ values = optionsTextArrayToRawValues(data);+ values = optionsParseRawValues(values, catalog, 0);+ options = optionsValuesToBytea(values, catalog);++ if (catalog->postprocess_fun)+ {+ catalog->postprocess_fun(options, false);+ }+ return options;+}diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.cindex ec10762..19816d5 100644--- a/src/backend/access/common/reloptions.c+++ b/src/backend/access/common/reloptions.c@@ -1,7 +1,10 @@
/*-------------------------------------------------------------------------
*
* reloptions.c
- * Core support for relation options (pg_class.reloptions)+ * Support for options relotions (pg_class.reloptions).+ * Reloptions for non-Access Metod relations are defined here.+ * There is also extractRelOptions function to extract reloptions+ * from pg_class table for all kind of relations.
*
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -17,928 +20,16 @@
#include <float.h>
-#include "access/gist_private.h"-#include "access/hash.h"
#include "access/htup_details.h"
-#include "access/nbtree.h"+#include "access/options.h"
#include "access/reloptions.h"
-#include "access/spgist.h"-#include "catalog/pg_type.h"-#include "commands/defrem.h"
#include "commands/tablespace.h"
-#include "commands/view.h"-#include "nodes/makefuncs.h"-#include "postmaster/postmaster.h"-#include "utils/array.h"
#include "utils/attoptcache.h"
-#include "utils/builtins.h"-#include "utils/guc.h"-#include "utils/memutils.h"
#include "utils/rel.h"
+#include "storage/bufmgr.h"-/*- * Contents of pg_class.reloptions- *- * To add an option:- *- * (i) decide on a type (integer, real, bool, string), name, default value,- * upper and lower bounds (if applicable); for strings, consider a validation- * routine.- * (ii) add a record below (or use add_<type>_reloption).- * (iii) add it to the appropriate options struct (perhaps StdRdOptions)- * (iv) add it to the appropriate handling routine (perhaps- * default_reloptions)- * (v) make sure the lock level is set correctly for that operation- * (vi) don't forget to document the option- *- * Note that we don't handle "oids" in relOpts because it is handled by- * interpretOidsOption().- *- * 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 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- },- /* 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_analyze_threshold",- "Minimum number of tuple inserts, updates or deletes prior to analyze",- RELOPT_KIND_HEAP,- ShareUpdateExclusiveLock- },- -1, 0, INT_MAX- },- {- {- "autovacuum_vacuum_cost_delay",- "Vacuum cost delay in milliseconds, for autovacuum",- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,- ShareUpdateExclusiveLock- },- -1, 0, 100- },- {- {- "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- },- {- {- "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- },- {- {- "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_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_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- },- /* list terminator */- {{NULL}}-};--static relopt_string stringRelOpts[] =-{- {- {- "buffering",- "Enables buffering build for this GiST index",- RELOPT_KIND_GIST,- AccessExclusiveLock- },- 4,- false,- gistValidateBufferingOption,- "auto"- },- {- {- "check_option",- "View has WITH CHECK OPTION defined (local or cascaded).",- RELOPT_KIND_VIEW,- AccessExclusiveLock- },- 0,- true,- validateWithCheckOption,- NULL- },- /* 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);--/*- * 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; 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; 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;-}--/*- * 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, char *name, char *desc)-{- MemoryContext oldcxt;- size_t size;- relopt_gen *newoption;-- oldcxt = MemoryContextSwitchTo(TopMemoryContext);-- 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_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;-- MemoryContextSwitchTo(oldcxt);-- return newoption;-}--/*- * add_bool_reloption- * Add a new boolean reloption- */-void-add_bool_reloption(bits32 kinds, char *name, char *desc, bool default_val)-{- relopt_bool *newoption;-- newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,- name, desc);- newoption->default_val = default_val;-- add_reloption((relopt_gen *) newoption);-}--/*- * add_int_reloption- * Add a new integer reloption- */-void-add_int_reloption(bits32 kinds, char *name, char *desc, int default_val,- int min_val, int max_val)-{- relopt_int *newoption;-- newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,- name, desc);- newoption->default_val = default_val;- newoption->min = min_val;- newoption->max = max_val;-- add_reloption((relopt_gen *) newoption);-}--/*- * add_real_reloption- * Add a new float reloption- */-void-add_real_reloption(bits32 kinds, char *name, char *desc, double default_val,- double min_val, double max_val)-{- relopt_real *newoption;-- newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,- name, desc);- newoption->default_val = default_val;- newoption->min = min_val;- newoption->max = max_val;-- add_reloption((relopt_gen *) 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, char *name, char *desc, char *default_val,- validate_string_relopt validator)-{- 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);- newoption->validate_cb = validator;- if (default_val)- {- 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_reloption((relopt_gen *) newoption);-}--/*- * 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 ignoreOids is true, then we should ignore any occurrence of "oids"- * in the list (it will be or has been handled by interpretOidsOption()).- *- * 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, char *namspace,- char *validnsps[], bool ignoreOids, 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, 'i',- &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 (pg_strcasecmp(def->defnamespace, namspace) != 0)- continue;-- kw_len = strlen(def->defname);- if (text_len > kw_len && text_str[kw_len] == '=' &&- pg_strncasecmp(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 (pg_strcasecmp(def->defnamespace,- validnsps[i]) == 0)- {- valid = true;- break;- }- }- }-- if (!valid)- ereport(ERROR,- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),- errmsg("unrecognized parameter namespace \"%s\"",- def->defnamespace)));- }-- if (ignoreOids && pg_strcasecmp(def->defname, "oids") == 0)- continue;-- /* ignore if not in the same namespace */- if (namspace == NULL)- {- if (def->defnamespace != NULL)- continue;- }- else if (def->defnamespace == NULL)- continue;- else if (pg_strcasecmp(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";- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);- /* +1 leaves room for sprintf's trailing null */- t = (text *) palloc(len + 1);- SET_VARSIZE(t, len);- sprintf(VARDATA(t), "%s=%s", def->defname, value);-- astate = accumArrayResult(astate, PointerGetDatum(t),- false, TEXTOID,- CurrentMemoryContext);- }- }-- if (astate)- result = makeArrayResult(astate, CurrentMemoryContext);- else- result = (Datum) 0;-- return result;-}---/*- * Convert the text-array format of reloptions into a List of DefElem.- * This is the inverse of transformRelOptions().- */-List *-untransformRelOptions(Datum options)-{- List *result = NIL;- ArrayType *array;- Datum *optiondatums;- int noptions;- int i;-- /* Nothing to do if no options */- if (!PointerIsValid(DatumGetPointer(options)))- return result;-- array = DatumGetArrayTypeP(options);-- deconstruct_array(array, TEXTOID, -1, false, '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;-}+void add_autovacuum_options(options_catalog * catalog, int base_offset,+ bool for_toast);
/*
* Extract and parse reloptions from a pg_class tuple.
@@ -954,11 +45,12 @@ untransformRelOptions(Datum options)
*/
bytea *
extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions)+ amrelopt_catalog_function catalog_fun)
{
- bytea *options;+ bytea *options = NULL;
bool isnull;
Datum datum;
+ options_catalog *catalog = NULL;
Form_pg_class classForm;
datum = fastgetattr(tuple,
@@ -974,548 +66,321 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
switch (classForm->relkind)
{
case RELKIND_RELATION:
- case RELKIND_TOASTVALUE:
case RELKIND_MATVIEW:
- case RELKIND_PARTITIONED_TABLE:- options = heap_reloptions(classForm->relkind, datum, false);+ catalog = get_heap_relopt_catalog();+ break;+ case RELKIND_TOASTVALUE:+ catalog = get_toast_relopt_catalog();
break;
case RELKIND_VIEW:
- options = view_reloptions(datum, false);+ catalog = get_view_relopt_catalog();
break;
case RELKIND_INDEX:
- options = index_reloptions(amoptions, datum, false);+ if (catalog_fun)+ catalog = catalog_fun();
break;
case RELKIND_FOREIGN_TABLE:
options = NULL;
break;
+ case RELKIND_PARTITIONED_TABLE:+ catalog = NULL; /* No options for parted table for now */+ break;
default:
Assert(false); /* can't get here */
options = NULL; /* keep compiler quiet */
break;
}
+ if (catalog)+ options = optionsTextArrayToBytea(catalog, datum);
return options;
}
-/*- * 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.- */-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)))- {- ArrayType *array = DatumGetArrayTypeP(options);- Datum *optiondatums;- int noptions;-- deconstruct_array(array, TEXTOID, -1, false, 'i',- &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] == '=' &&- pg_strncasecmp(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);- }-- *numrelopts = numoptions;- return reloptions;-}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's- * value+ * Here goes functions that build catalogs for non-index relations. Catalogs+ * for index relations are available from Access Methods, all other+ * relation catalogs are here
*/
-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);- 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_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.+ * Both heap and toast relation have almost the same autovacuum set of options.+ * These autovacuum options are stored in the same structure (AutoVacOpts)+ * that is a part of both HeapOptions and ToastOptions.
*
- * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or- * equivalent).- */-void *-allocateReloptStruct(Size base, relopt_value *options, int numoptions)-{- Size size = base;- int i;-- for (i = 0; i < numoptions; i++)- if (options[i].gen->type == RELOPT_TYPE_STRING)- size += GET_STRING_RELOPTION_LEN(options[i]) + 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.+ * Function add_autovacuum_options adds to the "catalog" catalog autovacuum+ * options definition. In binary mode this options are saved into AutoVacOpts+ * structure, which is located in result bytea chunk with base_offset offset.
*
- * 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.+ * Heap has two options (autovacuum_analyze_threshold and+ * autovacuum_analyze_scale_factor) that is not used for toast. So for toast+ * case this options are added to catalog with OPTION_DEFINITION_FLAG_REJECT+ * flag set, so postgres will reject this toast options from the CREATE or+ * ALTER command, but still will save default value in binary representation
*/
+
void
-fillRelOptions(void *rdopts, Size basesize,- relopt_value *options, int numoptions,- bool validate,- const relopt_parse_elt *elems, int numelems)+add_autovacuum_options(options_catalog * catalog, int base_offset,+ bool for_toast)
{
- int i;- int offset = basesize;-- for (i = 0; i < numoptions; i++)- {- int j;- bool found = false;-- for (j = 0; j < numelems; j++)- {- if (pg_strcasecmp(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_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 (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);+ optionsCatalogAddItemBool(catalog, "autovacuum_enabled",+ "Enables autovacuum in this relation",+ ShareUpdateExclusiveLock,+ 0, base_offset + offsetof(AutoVacOpts, enabled), true);++ optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_threshold",+ "Minimum number of tuple updates or deletes prior to vacuum",+ ShareUpdateExclusiveLock,+ 0, base_offset + offsetof(AutoVacOpts, vacuum_threshold),+ -1, 0, INT_MAX);++ optionsCatalogAddItemInt(catalog, "autovacuum_analyze_threshold",+ "Minimum number of tuple updates or deletes prior to vacuum",+ ShareUpdateExclusiveLock,+ for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0,+ base_offset + offsetof(AutoVacOpts, analyze_threshold),+ -1, 0, INT_MAX);++ optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_cost_delay",+ "Vacuum cost delay in milliseconds, for autovacuum",+ ShareUpdateExclusiveLock,+ 0, base_offset + offsetof(AutoVacOpts, vacuum_cost_delay),+ -1, 0, 100);++ optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_cost_limit",+ "Vacuum cost amount available before napping, for autovacuum",+ ShareUpdateExclusiveLock,+ 0, base_offset + offsetof(AutoVacOpts, vacuum_cost_limit),+ -1, 0, 10000);++ optionsCatalogAddItemInt(catalog, "autovacuum_freeze_min_age",+ "Minimum age at which VACUUM should freeze a table row, for autovacuum",+ ShareUpdateExclusiveLock,+ 0, base_offset + offsetof(AutoVacOpts, freeze_min_age),+ -1, 0, 1000000000);++ optionsCatalogAddItemInt(catalog, "autovacuum_freeze_max_age",+ "Age at which to autovacuum a table to prevent transaction ID wraparound",+ ShareUpdateExclusiveLock,+ 0, base_offset + offsetof(AutoVacOpts, freeze_max_age),+ -1, 100000, 2000000000);++ optionsCatalogAddItemInt(catalog, "autovacuum_freeze_table_age",+ "Age at which VACUUM should perform a full table sweep to freeze row versions",+ ShareUpdateExclusiveLock,+ 0, base_offset + offsetof(AutoVacOpts, freeze_table_age),+ -1, 0, 2000000000);++ optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_min_age",+ "Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",+ ShareUpdateExclusiveLock,+ 0, base_offset + offsetof(AutoVacOpts, multixact_freeze_min_age),+ -1, 0, 1000000000);++ optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_max_age",+ "Multixact age at which to autovacuum a table to prevent multixact wraparound",+ ShareUpdateExclusiveLock,+ 0, base_offset + offsetof(AutoVacOpts, multixact_freeze_max_age),+ -1, 10000, 2000000000);++ optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_table_age",+ "Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",+ ShareUpdateExclusiveLock,+ 0, base_offset + offsetof(AutoVacOpts, multixact_freeze_table_age),+ -1, 0, 2000000000);++ optionsCatalogAddItemInt(catalog, "log_autovacuum_min_duration",+ "Sets the minimum execution time above which autovacuum actions will be logged",+ ShareUpdateExclusiveLock,+ 0, base_offset + offsetof(AutoVacOpts, log_min_duration),+ -1, -1, INT_MAX);++ optionsCatalogAddItemReal(catalog, "autovacuum_vacuum_scale_factor",+ "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",+ ShareUpdateExclusiveLock,+ 0, base_offset + offsetof(AutoVacOpts, vacuum_scale_factor),+ -1, 0.0, 100.0);++ optionsCatalogAddItemReal(catalog, "autovacuum_analyze_scale_factor",+ "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",+ ShareUpdateExclusiveLock,+ for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0,+ base_offset + offsetof(AutoVacOpts, analyze_scale_factor),+ -1, 0.0, 100.0);
}
-
/*
- * Option parser for anything that uses StdRdOptions.+ * get_heap_relopt_catalog+ * Returns an options catalog for heap relation.
*/
-bytea *-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)-{- relopt_value *options;- StdRdOptions *rdopts;- int numoptions;- 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_analyze_threshold", RELOPT_TYPE_INT,- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},- {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT,- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)},- {"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)},- {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_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)}- };+static options_catalog *heap_relopt_catalog = NULL;- options = parseRelOptions(reloptions, validate, kind, &numoptions);-- /* if none set, we're done */- if (numoptions == 0)- return NULL;-- rdopts = allocateReloptStruct(sizeof(StdRdOptions), options, numoptions);-- fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numoptions,- validate, tab, lengthof(tab));-- pfree(options);-- return (bytea *) rdopts;+options_catalog *+get_heap_relopt_catalog(void)+{+ if (!heap_relopt_catalog)+ {+ heap_relopt_catalog = allocateOptionsCatalog(NULL, sizeof(HeapOptions),+ 14 + 4); /* 14 - for autovacuum+ * options, 4 - core+ * heap options */++ optionsCatalogAddItemInt(heap_relopt_catalog, "fillfactor",+ "Packs table pages only to this percentag",+ ShareUpdateExclusiveLock, /* since it applies only+ * to later inserts */+ 0, offsetof(HeapOptions, fillfactor),+ HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100);++ add_autovacuum_options(heap_relopt_catalog,+ offsetof(HeapOptions, autovacuum), false);++ optionsCatalogAddItemBool(heap_relopt_catalog, "user_catalog_table",+ "Declare a table as an additional catalog table, e.g. for the purpose of logical replication",+ AccessExclusiveLock,+ 0, offsetof(HeapOptions, user_catalog_table),+ false);++ optionsCatalogAddItemInt(heap_relopt_catalog, "parallel_workers",+ "Number of parallel processes that can be used per executor node for this relation",+ ShareUpdateExclusiveLock,+ 0, offsetof(HeapOptions, parallel_workers),+ -1, 0, 1024);++ /*+ * A WITH OID / WITHOUT OIDS expressions are converted by syntax+ * parser into "oids" relation option, but this option is processed by+ * interpretOidsOption(), that does not use core options code. That is+ * why here we should just ignore this option as if it does not exist.+ * Do it with OPTION_DEFINITION_FLAG_IGNORE flag+ */+ optionsCatalogAddItemBool(heap_relopt_catalog, "oids",+ "Option used for WITH OIDS expression. Processed elsewhere",+ NoLock,+ OPTION_DEFINITION_FLAG_IGNORE, -1,+ false);+ }+ return heap_relopt_catalog;
}
/*
- * Option parser for views+ * get_toast_relopt_catalog+ * Returns an options catalog for toast relation.
*/
-bytea *-view_reloptions(Datum reloptions, bool validate)-{- relopt_value *options;- ViewOptions *vopts;- int numoptions;- static const relopt_parse_elt tab[] = {- {"security_barrier", RELOPT_TYPE_BOOL,- offsetof(ViewOptions, security_barrier)},- {"check_option", RELOPT_TYPE_STRING,- offsetof(ViewOptions, check_option_offset)}- };-- options = parseRelOptions(reloptions, validate, RELOPT_KIND_VIEW, &numoptions);-- /* if none set, we're done */- if (numoptions == 0)- return NULL;- vopts = allocateReloptStruct(sizeof(ViewOptions), options, numoptions);+static options_catalog *toast_relopt_catalog = NULL;- fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numoptions,- validate, tab, lengthof(tab));-- pfree(options);-- return (bytea *) vopts;-}--/*- * Parse options for heaps, views and toast tables.- */-bytea *-heap_reloptions(char relkind, Datum reloptions, bool validate)+options_catalog *+get_toast_relopt_catalog(void)
{
- StdRdOptions *rdopts;-- switch (relkind)+ if (!toast_relopt_catalog)
{
- 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);- case RELKIND_PARTITIONED_TABLE:- return default_reloptions(reloptions, validate,- RELOPT_KIND_PARTITIONED);- default:- /* other relkinds are not supported */- return NULL;+ toast_relopt_catalog = allocateOptionsCatalog("toast", sizeof(ToastOptions),+ 14); /* 14 - for autovacuum+ * options */+ add_autovacuum_options(toast_relopt_catalog,+ offsetof(ToastOptions, autovacuum), true);
}
+ return toast_relopt_catalog;
}
-
/*
- * Parse options for indexes.- *- * amoptions index AM's option parser function- * reloptions options as text[] datum- * validate error flag+ * get_view_relopt_catalog+ * Returns an options catalog for view relation.
*/
-bytea *-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)+static options_catalog *view_relopt_catalog = NULL;++options_catalog *+get_view_relopt_catalog(void)
{
- Assert(amoptions != NULL);+ static const char *enum_names[] = VIEW_OPTION_CHECK_OPTION_VALUE_NAMES;- /* Assume function is strict */- if (!PointerIsValid(DatumGetPointer(reloptions)))- return NULL;+ if (!view_relopt_catalog)+ {+ view_relopt_catalog = allocateOptionsCatalog(NULL,+ sizeof(ViewOptions), 2);- return amoptions(reloptions, validate);+ optionsCatalogAddItemBool(view_relopt_catalog, "security_barrier",+ "View acts as a row security barrier",+ AccessExclusiveLock,+ 0, offsetof(ViewOptions, security_barrier), false);++ optionsCatalogAddItemEnum(view_relopt_catalog, "check_option",+ "View has WITH CHECK OPTION defined (local or cascaded)",+ AccessExclusiveLock, 0,+ offsetof(ViewOptions, check_option),+ enum_names,+ VIEW_OPTION_CHECK_OPTION_NOT_SET);+ }+ return view_relopt_catalog;
}
/*
- * Option parser for attribute reloptions+ * get_attribute_options_catalog+ * Returns an options catalog for heap attributes
*/
-bytea *-attribute_reloptions(Datum reloptions, bool validate)-{- relopt_value *options;- AttributeOpts *aopts;- int numoptions;- 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)}- };-- options = parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE,- &numoptions);-- /* if none set, we're done */- if (numoptions == 0)- return NULL;-- aopts = allocateReloptStruct(sizeof(AttributeOpts), options, numoptions);+static options_catalog *attribute_options_catalog = NULL;- fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,- validate, tab, lengthof(tab));+options_catalog *+get_attribute_options_catalog(void)+{+ if (!attribute_options_catalog)+ {+ attribute_options_catalog = allocateOptionsCatalog(NULL,+ sizeof(AttributeOpts), 2);- pfree(options);+ optionsCatalogAddItemReal(attribute_options_catalog, "n_distinct",+ "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations)",+ ShareUpdateExclusiveLock,+ 0, offsetof(AttributeOpts, n_distinct), 0, -1.0, DBL_MAX);- return (bytea *) aopts;+ optionsCatalogAddItemReal(attribute_options_catalog,+ "n_distinct_inherited",+ "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations",+ ShareUpdateExclusiveLock,+ 0, offsetof(AttributeOpts, n_distinct_inherited), 0, -1.0, DBL_MAX);+ }+ return attribute_options_catalog;
}
/*
- * Option parser for tablespace reloptions+ * get_tablespace_options_catalog+ * Returns an options catalog for tablespaces
*/
-bytea *-tablespace_reloptions(Datum reloptions, bool validate)-{- relopt_value *options;- TableSpaceOpts *tsopts;- int numoptions;- 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)}- };-- options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,- &numoptions);-- /* if none set, we're done */- if (numoptions == 0)- return NULL;-- tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);-- fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,- validate, tab, lengthof(tab));-- pfree(options);+static options_catalog *tablespace_options_catalog = NULL;- return (bytea *) tsopts;+options_catalog *+get_tablespace_options_catalog(void)+{+ if (!tablespace_options_catalog)+ {+ tablespace_options_catalog = allocateOptionsCatalog(NULL,+ sizeof(TableSpaceOpts), 3);++ optionsCatalogAddItemReal(tablespace_options_catalog,+ "random_page_cost",+ "Sets the planner's estimate of the cost of a nonsequentially fetched disk page",+ ShareUpdateExclusiveLock,+ 0, offsetof(TableSpaceOpts, random_page_cost), -1, 0.0, DBL_MAX);++ optionsCatalogAddItemReal(tablespace_options_catalog, "seq_page_cost",+ "Sets the planner's estimate of the cost of a sequentially fetched disk page",+ ShareUpdateExclusiveLock,+ 0, offsetof(TableSpaceOpts, seq_page_cost), -1, 0.0, DBL_MAX);++ optionsCatalogAddItemInt(tablespace_options_catalog,+ "effective_io_concurrency",+ "Number of simultaneous requests that can be handled efficiently by the disk subsystem",+ ShareUpdateExclusiveLock,+ 0, offsetof(TableSpaceOpts, effective_io_concurrency),+#ifdef USE_PREFETCH+ -1, 0, MAX_IO_CONCURRENCY+#else+ 0, 0, 0+#endif+ );+ }+ return tablespace_options_catalog;
}
/*
@@ -1525,33 +390,56 @@ 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_catalog *catalog = NULL;
if (defList == NIL)
return AccessExclusiveLock;
- if (need_initialization)- initialize_reloptions();+ switch (rel->rd_rel->relkind)+ {+ case RELKIND_TOASTVALUE:+ catalog = get_toast_relopt_catalog();+ break;+ case RELKIND_RELATION:+ case RELKIND_MATVIEW:+ catalog = get_heap_relopt_catalog();+ break;+ case RELKIND_INDEX:+ catalog = rel->rd_amroutine->amrelopt_catalog();+ break;+ case RELKIND_VIEW:+ catalog = get_view_relopt_catalog();+ break;+ case RELKIND_PARTITIONED_TABLE:+ catalog = NULL; /* No options for parted table for now */+ break;+ default:+ Assert(false); /* can't get here */+ break;+ }++ Assert(catalog); /* No catalog - no reloption change. Should+ * not get here */
foreach(cell, defList)
{
DefElem *def = (DefElem *) lfirst(cell);
+
int i;
- for (i = 0; relOpts[i]; i++)+ for (i = 0; i < catalog->num; i++)
{
- if (pg_strncasecmp(relOpts[i]->name,- def->defname,- relOpts[i]->namelen + 1) == 0)- {- if (lockmode < relOpts[i]->lockmode)- lockmode = relOpts[i]->lockmode;- }+ option_definition_basic *gen = catalog->definitions[i];++ if (pg_strcasecmp(gen->name,+ def->defname) == 0)+ if (lockmode < gen->lockmode)+ lockmode = gen->lockmode;
}
}
-
return lockmode;
}
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.cindex 136ea27..16b96c6 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"
@@ -26,7 +26,8 @@
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/typcache.h"
-+#include "utils/memutils.h"+#include "utils/guc.h"
/*
* GIN handler function: return IndexAmRoutine with access method parameters
@@ -60,7 +61,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = ginvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = ginvalidate;
amroutine->ambeginscan = ginbeginscan;
@@ -70,6 +70,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = ginendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = gingetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -595,35 +596,6 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
return entries;
}
-bytea *-ginoptions(Datum reloptions, bool validate)-{- relopt_value *options;- GinOptions *rdopts;- int numoptions;- static const relopt_parse_elt tab[] = {- {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)},- {"gin_pending_list_limit", RELOPT_TYPE_INT, offsetof(GinOptions,- pendingListCleanupSize)}- };-- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,- &numoptions);-- /* if none set, we're done */- if (numoptions == 0)- return NULL;-- rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);-- fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numoptions,- validate, tab, lengthof(tab));-- pfree(options);-- return (bytea *) rdopts;-}-
/*
* Fetch index's statistical data into *stats
*
@@ -700,3 +672,32 @@ ginUpdateStats(Relation index, const GinStatsData *stats)
END_CRIT_SECTION();
}
+++static options_catalog *gin_relopt_catalog = NULL;++void *+gingetreloptcatalog(void)+{+ if (!gin_relopt_catalog)+ {+ gin_relopt_catalog = allocateOptionsCatalog(NULL,+ sizeof(GinOptions), 2);++ optionsCatalogAddItemBool(gin_relopt_catalog, "fastupdate",+ "Enables \"fast update\" feature for this GIN index",+ AccessExclusiveLock,+ 0,+ offsetof(GinOptions, useFastUpdate),+ GIN_DEFAULT_USE_FASTUPDATE);++ optionsCatalogAddItemInt(gin_relopt_catalog, "gin_pending_list_limit",+ "Maximum size of the pending list for this GIN index, in kilobytes",+ AccessExclusiveLock,+ 0,+ offsetof(GinOptions, pendingListCleanupSize),+ -1, 64, MAX_KILOBYTES);++ }+ return gin_relopt_catalog;+}diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.cindex aec174c..c42a375 100644--- a/src/backend/access/gist/gist.c+++ b/src/backend/access/gist/gist.c@@ -81,7 +81,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = gistvacuumcleanup;
amroutine->amcanreturn = gistcanreturn;
amroutine->amcostestimate = gistcostestimate;
- amroutine->amoptions = gistoptions;
amroutine->amproperty = gistproperty;
amroutine->amvalidate = gistvalidate;
amroutine->ambeginscan = gistbeginscan;
@@ -91,6 +90,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = gistendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = gistgetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.cindex b4cb364..d80d604 100644--- a/src/backend/access/gist/gistbuild.c+++ b/src/backend/access/gist/gistbuild.c@@ -126,11 +126,10 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
{
/* Get buffering mode from the options string */
GiSTOptions *options = (GiSTOptions *) index->rd_options;
- char *bufferingMode = (char *) options + options->bufferingModeOffset;- if (strcmp(bufferingMode, "on") == 0)+ if (options->buffering_mode == GIST_OPTION_BUFFERING_ON)
buildstate.bufferingMode = GIST_BUFFERING_STATS;
- else if (strcmp(bufferingMode, "off") == 0)+ else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF)
buildstate.bufferingMode = GIST_BUFFERING_DISABLED;
else
buildstate.bufferingMode = GIST_BUFFERING_AUTO;
@@ -234,25 +233,6 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
}
/*
- * Validator for "buffering" reloption on GiST indexes. Allows "on", "off"- * and "auto" values.- */-void-gistValidateBufferingOption(char *value)-{- if (value == NULL ||- (strcmp(value, "on") != 0 &&- strcmp(value, "off") != 0 &&- strcmp(value, "auto") != 0))- {- ereport(ERROR,- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),- errmsg("invalid value for \"buffering\" option"),- errdetail("Valid values are \"on\", \"off\", and \"auto\".")));- }-}--/*
* Attempt to switch to buffering mode.
*
* If there is not enough memory for buffering build, sets bufferingMode
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.cindex 26d89f7..ee322ba 100644--- a/src/backend/access/gist/gistutil.c+++ b/src/backend/access/gist/gistutil.c@@ -18,14 +18,13 @@
#include "access/gist_private.h"
#include "access/htup_details.h"
-#include "access/reloptions.h"+#include "access/options.h"
#include "catalog/pg_opclass.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
-
/*
* Write itup vector to page, has no control of free space.
*/
@@ -832,34 +831,6 @@ gistNewBuffer(Relation r)
return buffer;
}
-bytea *-gistoptions(Datum reloptions, bool validate)-{- relopt_value *options;- GiSTOptions *rdopts;- int numoptions;- static const relopt_parse_elt tab[] = {- {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},- {"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOffset)}- };-- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST,- &numoptions);-- /* if none set, we're done */- if (numoptions == 0)- return NULL;-- rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);-- fillRelOptions((void *) rdopts, sizeof(GiSTOptions), options, numoptions,- validate, tab, lengthof(tab));-- pfree(options);-- return (bytea *) rdopts;-}-
/*
* gistproperty() -- Check boolean properties of indexes.
*
@@ -999,3 +970,33 @@ gistGetFakeLSN(Relation rel)
return GetFakeLSNForUnloggedRel();
}
}
++static options_catalog *gist_relopt_catalog = NULL;++void *+gistgetreloptcatalog(void)+{+ static const char *enum_names[] = GIST_OPTION_BUFFERING_VALUE_NAMES;++ if (!gist_relopt_catalog)+ {+ gist_relopt_catalog = allocateOptionsCatalog(NULL,+ sizeof(GiSTOptions), 2);++ optionsCatalogAddItemInt(gist_relopt_catalog, "fillfactor",+ "Packs gist index pages only to this percentage",+ NoLock, /* No ALTER, no lock */+ OPTION_DEFINITION_FLAG_FORBID_ALTER,+ offsetof(GiSTOptions, fillfactor),+ GIST_DEFAULT_FILLFACTOR,+ GIST_MIN_FILLFACTOR, 100);+ optionsCatalogAddItemEnum(gist_relopt_catalog, "buffering",+ "Enables buffering build for this GiST index",+ NoLock, /* No ALTER, no lock */+ OPTION_DEFINITION_FLAG_FORBID_ALTER,+ offsetof(GiSTOptions, buffering_mode),+ enum_names,+ GIST_OPTION_BUFFERING_AUTO);+ }+ return gist_relopt_catalog;+}diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.cindex 0fef60a..75c0ba9 100644--- a/src/backend/access/hash/hash.c+++ b/src/backend/access/hash/hash.c@@ -79,7 +79,6 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = hashvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = hashcostestimate;
- amroutine->amoptions = hashoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = hashvalidate;
amroutine->ambeginscan = hashbeginscan;
@@ -89,6 +88,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = hashendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = hashgetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.cindex f279dce..a841cfc 100644--- a/src/backend/access/hash/hashpage.c+++ b/src/backend/access/hash/hashpage.c@@ -368,7 +368,7 @@ _hash_init(Relation rel, double num_tuples, ForkNumber forkNum)
data_width = sizeof(uint32);
item_width = MAXALIGN(sizeof(IndexTupleData)) + MAXALIGN(data_width) +
sizeof(ItemIdData); /* include the line pointer */
- ffactor = RelationGetTargetPageUsage(rel, HASH_DEFAULT_FILLFACTOR) / item_width;+ ffactor = (BLCKSZ * HashGetFillFactor(rel) / 100) / item_width;
/* keep to a sane range */
if (ffactor < 10)
ffactor = 10;
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.cindex f2a1c5b..eb45362 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 "utils/lsyscache.h"
#include "utils/rel.h"
@@ -286,12 +286,6 @@ _hash_checkpage(Relation rel, Buffer buf, int flags)
}
}
-bytea *-hashoptions(Datum reloptions, bool validate)-{- return default_reloptions(reloptions, validate, RELOPT_KIND_HASH);-}-
/*
* _hash_get_indextuple_hashkey - get the hash index tuple's hash key value
*/
@@ -627,3 +621,28 @@ _hash_kill_items(IndexScanDesc scan)
else
_hash_relbuf(rel, buf);
}
++static options_catalog *hash_relopt_catalog = NULL;++void *+hashgetreloptcatalog(void)+{+ if (!hash_relopt_catalog)+ {+ hash_relopt_catalog = allocateOptionsCatalog(NULL,+ sizeof(HashRelOptions), 1);++ optionsCatalogAddItemInt(hash_relopt_catalog, "fillfactor",+ "Packs hash index pages only to this percentage",+ NoLock, /* No ALTER -- no lock */+ OPTION_DEFINITION_FLAG_FORBID_ALTER, /* fillfactor is actualy+ * stored in metapage+ * and should not be+ * changed once index is+ * created */+ offsetof(HashRelOptions, fillfactor),+ HASH_DEFAULT_FILLFACTOR,+ HASH_MIN_FILLFACTOR, 100);+ }+ return hash_relopt_catalog;+}diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.cindex 52dda41..9e19de2 100644--- a/src/backend/access/heap/heapam.c+++ b/src/backend/access/heap/heapam.c@@ -2732,7 +2732,10 @@ heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,+ if (IsToastRelation(relation))+ saveFreeSpace = ToastGetTargetPageFreeSpace();+ else+ saveFreeSpace = HeapGetTargetPageFreeSpace(relation,
HEAP_DEFAULT_FILLFACTOR);
/* Toast and set header data in all the tuples */
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.cindex 13e3bdc..8c8f023 100644--- a/src/backend/access/heap/hio.c+++ b/src/backend/access/heap/hio.c@@ -323,7 +323,10 @@ RelationGetBufferForTuple(Relation relation, Size len,
len, MaxHeapTupleSize)));
/* Compute desired extra freespace due to fillfactor option */
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,+ if (IsToastRelation(relation))+ saveFreeSpace = ToastGetTargetPageFreeSpace();+ else+ saveFreeSpace = HeapGetTargetPageFreeSpace(relation,
HEAP_DEFAULT_FILLFACTOR);
if (otherBuffer != InvalidBuffer)
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.cindex 7753ee7..212dcfe 100644--- a/src/backend/access/heap/pruneheap.c+++ b/src/backend/access/heap/pruneheap.c@@ -130,7 +130,11 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
* important than sometimes getting a wrong answer in what is after all
* just a heuristic estimate.
*/
- minfree = RelationGetTargetPageFreeSpace(relation,++ if (IsToastRelation(relation))+ minfree = ToastGetTargetPageFreeSpace();+ else+ minfree = HeapGetTargetPageFreeSpace(relation,
HEAP_DEFAULT_FILLFACTOR);
minfree = Max(minfree, BLCKSZ / 10);
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.cindex f93c194..71bf1a1 100644--- a/src/backend/access/heap/rewriteheap.c+++ b/src/backend/access/heap/rewriteheap.c@@ -667,7 +667,10 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
len, MaxHeapTupleSize)));
/* Compute desired extra freespace due to fillfactor option */
- saveFreeSpace = RelationGetTargetPageFreeSpace(state->rs_new_rel,+ if (IsToastRelation(state->rs_new_rel))+ saveFreeSpace = ToastGetTargetPageFreeSpace();+ else+ saveFreeSpace = HeapGetTargetPageFreeSpace(state->rs_new_rel,
HEAP_DEFAULT_FILLFACTOR);
/* Now we can check to see if there's enough free space already. */
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.cindex bf963fc..4272e60 100644--- a/src/backend/access/nbtree/nbtinsert.c+++ b/src/backend/access/nbtree/nbtinsert.c@@ -1427,8 +1427,7 @@ _bt_findsplitloc(Relation rel,
state.is_rightmost = P_RIGHTMOST(opaque);
state.have_split = false;
if (state.is_leaf)
- state.fillfactor = RelationGetFillFactor(rel,- BTREE_DEFAULT_FILLFACTOR);+ state.fillfactor = BTGetFillFactor(rel);
else
state.fillfactor = BTREE_NONLEAF_FILLFACTOR;
state.newitemonleft = false; /* these just to keep compiler quiet */
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.cindex 3dbafdd..d164e6c 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/xlog.h"
+#include "access/reloptions.h"
#include "catalog/index.h"
#include "commands/vacuum.h"
#include "pgstat.h"
@@ -34,7 +35,6 @@
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
-
/* Working state for btbuild and its callback */
typedef struct
{
@@ -115,8 +115,7 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
BTCycleId cycleid);
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
--+static void * btgetreloptcatalog (void);
/*
* Btree handler function: return IndexAmRoutine with access method parameters
* and callbacks.
@@ -149,7 +148,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = btvacuumcleanup;
amroutine->amcanreturn = btcanreturn;
amroutine->amcostestimate = btcostestimate;
- amroutine->amoptions = btoptions;
amroutine->amproperty = btproperty;
amroutine->amvalidate = btvalidate;
amroutine->ambeginscan = btbeginscan;
@@ -159,6 +157,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = btendscan;
amroutine->ammarkpos = btmarkpos;
amroutine->amrestrpos = btrestrpos;
+ amroutine->amrelopt_catalog = btgetreloptcatalog;
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
@@ -1390,3 +1389,26 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+++static options_catalog *bt_relopt_catalog = NULL;++static void *+btgetreloptcatalog(void)+{+ if (!bt_relopt_catalog)+ {+ bt_relopt_catalog = allocateOptionsCatalog(NULL,+ sizeof(BTRelOptions), 1);++ optionsCatalogAddItemInt(bt_relopt_catalog, "fillfactor",+ "Packs btree index pages only to this percentage",+ ShareUpdateExclusiveLock, /* since it applies only+ * to later inserts */+ 0,+ offsetof(BTRelOptions, fillfactor),+ BTREE_DEFAULT_FILLFACTOR,+ BTREE_MIN_FILLFACTOR, 100);+ }+ return bt_relopt_catalog;+}diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.cindex bf6c03c..5c6957a 100644--- a/src/backend/access/nbtree/nbtsort.c+++ b/src/backend/access/nbtree/nbtsort.c@@ -344,8 +344,8 @@ _bt_pagestate(BTWriteState *wstate, uint32 level)
if (level > 0)
state->btps_full = (BLCKSZ * (100 - BTREE_NONLEAF_FILLFACTOR) / 100);
else
- state->btps_full = RelationGetTargetPageFreeSpace(wstate->index,- BTREE_DEFAULT_FILLFACTOR);+ state->btps_full = (BLCKSZ * (100 - BTGetFillFactor(wstate->index)) / 100);+
/* no parent level, yet */
state->btps_next = NULL;
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.cindex dbfb775..b01d22e 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 "miscadmin.h"
#include "utils/array.h"
@@ -2038,12 +2038,6 @@ BTreeShmemInit(void)
Assert(found);
}
-bytea *-btoptions(Datum reloptions, bool validate)-{- return default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);-}-
/*
* btproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.cindex 22f64b0..77845e5 100644--- a/src/backend/access/spgist/spgutils.c+++ b/src/backend/access/spgist/spgutils.c@@ -15,7 +15,7 @@
#include "postgres.h"
-#include "access/reloptions.h"+#include "access/options.h"
#include "access/spgist_private.h"
#include "access/transam.h"
#include "access/xact.h"
@@ -59,7 +59,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = spgvacuumcleanup;
amroutine->amcanreturn = spgcanreturn;
amroutine->amcostestimate = spgcostestimate;
- amroutine->amoptions = spgoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = spgvalidate;
amroutine->ambeginscan = spgbeginscan;
@@ -69,6 +68,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amendscan = spgendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = spggetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -372,8 +372,8 @@ SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
* related to the ones already on it. But fillfactor mustn't cause an
* error for requests that would otherwise be legal.
*/
- needSpace += RelationGetTargetPageFreeSpace(index,- SPGIST_DEFAULT_FILLFACTOR);+ needSpace += (BLCKSZ * (100 - SpGistGetFillFactor(index)) / 100);+
needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
/* Get the cache entry for this flags setting */
@@ -537,15 +537,6 @@ SpGistInitMetapage(Page page)
}
/*
- * reloptions processing for SPGiST- */-bytea *-spgoptions(Datum reloptions, bool validate)-{- return default_reloptions(reloptions, validate, RELOPT_KIND_SPGIST);-}--/*
* Get the space needed to store a non-null datum of the indicated type.
* Note the result is already rounded up to a MAXALIGN boundary.
* Also, we follow the SPGiST convention that pass-by-val types are
@@ -911,3 +902,27 @@ SpGistPageAddNewItem(SpGistState *state, Page page, Item item, Size size,
return offnum;
}
++++static options_catalog *spgist_relopt_catalog = NULL;++void *+spggetreloptcatalog(void)+{+ if (!spgist_relopt_catalog)+ {+ spgist_relopt_catalog = allocateOptionsCatalog(NULL,+ sizeof(SpGistRelOptions), 1);++ optionsCatalogAddItemInt(spgist_relopt_catalog, "fillfactor",+ "Packs spgist index pages only to this percentage",+ ShareUpdateExclusiveLock, /* since it applies only+ * to later inserts */+ 0,+ offsetof(SpGistRelOptions, fillfactor),+ SPGIST_DEFAULT_FILLFACTOR,+ SPGIST_MIN_FILLFACTOR, 100);+ }+ return spgist_relopt_catalog;+}diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.cindex 6f517bb..c7ba09c 100644--- a/src/backend/catalog/toasting.c+++ b/src/backend/catalog/toasting.c@@ -36,7 +36,7 @@
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_toast_pg_type_oid = InvalidOid;
-static void CheckAndCreateToastTable(Oid relOid, Datum reloptions,+static bool CheckAndCreateToastTable(Oid relOid, Datum reloptions,
LOCKMODE lockmode, bool check);
static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
Datum reloptions, LOCKMODE lockmode, bool check);
@@ -67,23 +67,26 @@ NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
CheckAndCreateToastTable(relOid, reloptions, lockmode, false);
}
-void+bool
NewRelationCreateToastTable(Oid relOid, Datum reloptions)
{
- CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, false);+ return CheckAndCreateToastTable(relOid, reloptions,+ AccessExclusiveLock, false);
}
-static void+static bool
CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, bool check)
{
Relation rel;
+ bool success;
rel = heap_open(relOid, lockmode);
/* create_toast_table does all the work */
- (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check);+ success = create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check);
heap_close(rel, NoLock);
+ return success;
}
/*
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.cindex 4d77411..85c517a 100644--- a/src/backend/commands/createas.c+++ b/src/backend/commands/createas.c@@ -89,6 +89,8 @@ create_ctas_internal(List *attrList, IntoClause *into)
Datum toast_options;
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
ObjectAddress intoRelationAddr;
+ List *toastDefList;+ bool toast_created;
/* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
is_matview = (into->viewQuery != NULL);
@@ -122,15 +124,20 @@ 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);- NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options);+ optionsDefListValdateNamespaces(create->options, validnsps);+ toastDefList = optionsDefListFilterNamespaces(create->options, "toast");++ toast_options = transformOptions(get_toast_relopt_catalog(), (Datum) 0,+ toastDefList, 0);++ toast_created = NewRelationCreateToastTable(intoRelationAddr.objectId,+ toast_options);+ if (!toast_created && toast_options)+ ereport(ERROR,+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),+ errmsg("no TOAST relation was created for a table. Can't set toast.* storage parameters")));
/* Create the "view" part of a materialized view. */
if (is_matview)
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.cindex 9ad9915..6f78d7b 100644--- a/src/backend/commands/foreigncmds.c+++ b/src/backend/commands/foreigncmds.c@@ -111,7 +111,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.cindex 3f615b6..0533170 100644--- a/src/backend/commands/indexcmds.c+++ b/src/backend/commands/indexcmds.c@@ -329,7 +329,7 @@ DefineIndex(Oid relationId,
Form_pg_am accessMethodForm;
IndexAmRoutine *amRoutine;
bool amcanorder;
- amoptions_function amoptions;+ amrelopt_catalog_function amoption_catalog;
Datum reloptions;
int16 *coloptions;
IndexInfo *indexInfo;
@@ -535,7 +535,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;+ amoption_catalog = amRoutine->amrelopt_catalog;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -549,10 +549,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 (amoption_catalog)+ {+ reloptions = transformOptions(amoption_catalog(),+ (Datum) 0, stmt->options, 0);+ }+ else+ {+ ereport(ERROR,+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),+ errmsg("access method %s does not support options",+ accessMethodName)));+ }
/*
* Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.cindex 2d4dcd7..0d27b30 100644--- a/src/backend/commands/tablecmds.c+++ b/src/backend/commands/tablecmds.c@@ -519,7 +519,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
@@ -605,13 +604,23 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
/*
* Parse and validate reloptions, if any.
*/
- reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,- true, false);-
if (relkind == RELKIND_VIEW)
- (void) view_reloptions(reloptions, true);+ {+ reloptions = transformOptions(+ get_view_relopt_catalog(),+ (Datum) 0, stmt->options, 0);+ }
else
- (void) heap_reloptions(relkind, reloptions, true);+ {+ /* If it is not view, then it if heap */+ char *namespaces[] = HEAP_RELOPT_NAMESPACES;+ List *heapDefList;++ optionsDefListValdateNamespaces(stmt->options, namespaces);+ heapDefList = optionsDefListFilterNamespaces(stmt->options, NULL);+ reloptions = transformOptions(get_heap_relopt_catalog(),+ (Datum) 0, heapDefList, 0);+ }
if (stmt->ofTypename)
{
@@ -3165,7 +3174,8 @@ void
AlterTableInternal(Oid relid, List *cmds, bool recurse)
{
Relation rel;
- LOCKMODE lockmode = AlterTableGetLockLevel(cmds);++ LOCKMODE lockmode = AlterTableGetLockLevel(relid, cmds);
rel = relation_open(relid, lockmode);
@@ -3207,13 +3217,15 @@ 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.
*/
ListCell *lcmd;
LOCKMODE lockmode = ShareUpdateExclusiveLock;
+ Relation rel;
foreach(lcmd, cmds)
{
@@ -3424,7 +3436,10 @@ AlterTableGetLockLevel(List *cmds)
* getTables() */
case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
* getTables() */
- cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);+ rel = relation_open(relid, NoLock);+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,+ castNode(List, cmd->def));+ relation_close(rel,NoLock);
break;
case AT_AttachPartition:
@@ -4199,7 +4214,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
errmsg("cannot rewrite system relation \"%s\"",
RelationGetRelationName(OldHeap))));
- if (RelationIsUsedAsCatalogTable(OldHeap))+ if (HeapIsUsedAsCatalogTable(OldHeap))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot rewrite table \"%s\" used as a catalog table",
@@ -6336,11 +6351,11 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options,
/* Generate new proposed attoptions (text array) */
datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
&isnull);
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,- castNode(List, options), NULL, NULL,- false, isReset);- /* Validate new options */- (void) attribute_reloptions(newOptions, true);++ newOptions = transformOptions(get_attribute_options_catalog(),+ isnull ? (Datum) 0 : datum,+ castNode(List, options), OPTIONS_PARSE_MODE_FOR_ALTER |+ (isReset ? OPTIONS_PARSE_MODE_FOR_RESET : 0));
/* Build new tuple. */
memset(repl_null, false, sizeof(repl_null));
@@ -10368,7 +10383,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
Datum repl_val[Natts_pg_class];
bool repl_null[Natts_pg_class];
bool repl_repl[Natts_pg_class];
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;+ options_parse_mode parse_mode;
if (defList == NIL && operation != AT_ReplaceRelOptions)
return; /* nothing to do */
@@ -10397,25 +10412,52 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
&isnull);
}
- /* Generate new proposed reloptions (text array) */- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,- defList, NULL, validnsps, false,- operation == AT_ResetRelOptions);-
/* Validate */
+ parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;+ if (operation == AT_ResetRelOptions)+ parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;+
switch (rel->rd_rel->relkind)
{
case RELKIND_RELATION:
case RELKIND_TOASTVALUE:
case RELKIND_MATVIEW:
case RELKIND_PARTITIONED_TABLE:
- (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);+ {+ char *namespaces[] = HEAP_RELOPT_NAMESPACES;+ List *heapDefList;++ optionsDefListValdateNamespaces(defList, namespaces);+ heapDefList = optionsDefListFilterNamespaces(+ defList, NULL);+ newOptions = transformOptions(get_heap_relopt_catalog(),+ isnull ? (Datum) 0 : datum,+ heapDefList, parse_mode);+ }
break;
case RELKIND_VIEW:
- (void) view_reloptions(newOptions, true);+ {+ newOptions = transformOptions(+ get_view_relopt_catalog(),+ isnull ? (Datum) 0 : datum,+ defList, parse_mode);+ }
break;
case RELKIND_INDEX:
- (void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);+ if (rel->rd_amroutine->amrelopt_catalog)+ {+ newOptions = transformOptions(+ rel->rd_amroutine->amrelopt_catalog(),+ isnull ? (Datum) 0 : datum,+ defList, parse_mode);+ }+ else+ {+ ereport(ERROR,+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),+ errmsg("index %s does not support options",+ RelationGetRelationName(rel))));+ }
break;
default:
ereport(ERROR,
@@ -10429,7 +10471,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
if (rel->rd_rel->relkind == RELKIND_VIEW)
{
Query *view_query = get_view_query(rel);
- List *view_options = untransformRelOptions(newOptions);+ List *view_options = optionsTextArrayToDefList(newOptions);
ListCell *cell;
bool check_option = false;
@@ -10489,6 +10531,8 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
{
Relation toastrel;
Oid toastid = rel->rd_rel->reltoastrelid;
+ List *toastDefList;+ options_parse_mode parse_mode;
toastrel = heap_open(toastid, lockmode);
@@ -10512,12 +10556,15 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
&isnull);
}
+ parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;+ if (operation == AT_ResetRelOptions)+ parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,- defList, "toast", validnsps, false,- operation == AT_ResetRelOptions);+ toastDefList = optionsDefListFilterNamespaces(defList, "toast");- (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);+ newOptions = transformOptions(get_toast_relopt_catalog(),+ isnull ? (Datum) 0 : datum,+ toastDefList, parse_mode);
memset(repl_val, 0, sizeof(repl_val));
memset(repl_null, false, sizeof(repl_null));
@@ -10545,6 +10592,18 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
heap_close(toastrel, NoLock);
}
+ else+ {+ List *toastDefList;++ toastDefList = optionsDefListFilterNamespaces(defList, "toast");+ if (toastDefList)+ {+ ereport(ERROR,+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),+ errmsg("TOAST relation for this table does not exist. Can't change toast.* storage parameters")));+ }+ }
heap_close(pgclass, RowExclusiveLock);
}
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.cindex 8559c3b..3498fef 100644--- a/src/backend/commands/tablespace.c+++ b/src/backend/commands/tablespace.c@@ -332,10 +332,11 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
nulls[Anum_pg_tablespace_spcacl - 1] = true;
/* Generate new proposed spcoptions (text array) */
- newOptions = transformRelOptions((Datum) 0,- stmt->options,- NULL, NULL, false, false);- (void) tablespace_reloptions(newOptions, true);++ newOptions = transformOptions(+ get_tablespace_options_catalog(),+ (Datum) 0, stmt->options, 0);+
if (newOptions != (Datum) 0)
values[Anum_pg_tablespace_spcoptions - 1] = newOptions;
else
@@ -1023,10 +1024,13 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
/* Generate new proposed spcoptions (text array) */
datum = heap_getattr(tup, Anum_pg_tablespace_spcoptions,
RelationGetDescr(rel), &isnull);
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,- stmt->options, NULL, NULL, false,- stmt->isReset);- (void) tablespace_reloptions(newOptions, true);+++ newOptions = transformOptions(get_tablespace_options_catalog(),+ isnull ? (Datum) 0 : datum,+ stmt->options,+ OPTIONS_PARSE_MODE_FOR_ALTER |+ (stmt->isReset ? OPTIONS_PARSE_MODE_FOR_RESET : 0));
/* Build new tuple. */
memset(repl_null, false, sizeof(repl_null));
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.cindex a113bf5..eb564fd 100644--- a/src/backend/foreign/foreign.c+++ b/src/backend/foreign/foreign.c@@ -62,7 +62,7 @@ GetForeignDataWrapper(Oid fdwid)
if (isnull)
fdw->options = NIL;
else
- fdw->options = untransformRelOptions(datum);+ fdw->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -133,7 +133,7 @@ GetForeignServer(Oid serverid)
if (isnull)
server->options = NIL;
else
- server->options = untransformRelOptions(datum);+ server->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -201,7 +201,7 @@ GetUserMapping(Oid userid, Oid serverid)
if (isnull)
um->options = NIL;
else
- um->options = untransformRelOptions(datum);+ um->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -238,7 +238,7 @@ GetForeignTable(Oid relid)
if (isnull)
ft->options = NIL;
else
- ft->options = untransformRelOptions(datum);+ ft->options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -271,7 +271,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum)
if (isnull)
options = NIL;
else
- options = untransformRelOptions(datum);+ options = optionsTextArrayToDefList(datum);
ReleaseSysCache(tp);
@@ -540,7 +540,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;
}
@@ -611,7 +611,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/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.cindex 9d35a41..871dccc 100644--- a/src/backend/optimizer/util/plancat.c+++ b/src/backend/optimizer/util/plancat.c@@ -145,7 +145,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
&rel->pages, &rel->tuples, &rel->allvisfrac);
/* Retrieve the parallel_workers reloption, or -1 if not set. */
- rel->rel_parallel_workers = RelationGetParallelWorkers(relation, -1);+ rel->rel_parallel_workers = HeapGetParallelWorkers(relation, -1);
/*
* Make list of indexes. Ignore indexes on system catalogs if told to.
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.cindex af99e65..5150846 100644--- a/src/backend/parser/parse_clause.c+++ b/src/backend/parser/parse_clause.c@@ -3128,7 +3128,7 @@ transformOnConflictArbiter(ParseState *pstate,
exprLocation((Node *) onConflictClause))));
/* Same applies to table used by logical decoding as catalog table */
- if (RelationIsUsedAsCatalogTable(pstate->p_target_relation))+ if (HeapIsUsedAsCatalogTable(pstate->p_target_relation))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("ON CONFLICT is not supported on table \"%s\" used as a catalog table",
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.cindex 27e568f..5fa2696 100644--- a/src/backend/parser/parse_utilcmd.c+++ b/src/backend/parser/parse_utilcmd.c@@ -1524,7 +1524,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, 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/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.cindex c04c0b5..e805bef 100644--- a/src/backend/postmaster/autovacuum.c+++ b/src/backend/postmaster/autovacuum.c@@ -2704,6 +2704,7 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
{
bytea *relopts;
AutoVacOpts *av;
+ AutoVacOpts *src;
Assert(((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_RELATION ||
((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW ||
@@ -2713,8 +2714,13 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
if (relopts == NULL)
return NULL;
+ if (((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE)+ src = &(((ToastOptions *) relopts)->autovacuum);+ else+ src = &(((HeapOptions *) relopts)->autovacuum);+
av = palloc(sizeof(AutoVacOpts));
- memcpy(av, &(((StdRdOptions *) relopts)->autovacuum), sizeof(AutoVacOpts));+ memcpy(av, src, sizeof(AutoVacOpts));
pfree(relopts);
return av;
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.cindex 7a61af7..5ac9087 100644--- a/src/backend/rewrite/rewriteHandler.c+++ b/src/backend/rewrite/rewriteHandler.c@@ -1562,7 +1562,7 @@ ApplyRetrieveRule(Query *parsetree,
rte->rtekind = RTE_SUBQUERY;
rte->relid = InvalidOid;
- rte->security_barrier = RelationIsSecurityView(relation);+ rte->security_barrier = ViewIsSecurityView(relation);
rte->subquery = rule_action;
rte->inh = false; /* must not be set for a subquery */
@@ -3066,7 +3066,7 @@ rewriteTargetView(Query *parsetree, Relation view)
ChangeVarNodes(viewqual, base_rt_index, new_rt_index, 0);
- if (RelationIsSecurityView(view))+ if (ViewIsSecurityView(view))
{
/*
* The view's quals go in front of existing barrier quals: those
@@ -3102,8 +3102,9 @@ rewriteTargetView(Query *parsetree, Relation view)
*/
if (parsetree->commandType != CMD_DELETE)
{
- bool has_wco = RelationHasCheckOption(view);- bool cascaded = RelationHasCascadedCheckOption(view);++ bool has_wco = ViewHasCheckOption(view);+ bool cascaded = ViewHasCascadedCheckOption(view);
/*
* If the parent view has a cascaded check option, treat this view as
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.cindex 82a707a..9f1ca86 100644--- a/src/backend/tcop/utility.c+++ b/src/backend/tcop/utility.c@@ -1003,6 +1003,8 @@ ProcessUtilitySlow(ParseState *pstate,
{
Datum toast_options;
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+ List *toastDefList;+ bool success;
/* Create the table itself */
address = DefineRelation((CreateStmt *) stmt,
@@ -1023,18 +1025,24 @@ ProcessUtilitySlow(ParseState *pstate,
* parse and validate reloptions for the toast
* table
*/
- toast_options = transformRelOptions((Datum) 0,- ((CreateStmt *) stmt)->options,- "toast",- validnsps,- true,- false);- (void) heap_reloptions(RELKIND_TOASTVALUE,- toast_options,- true);- NewRelationCreateToastTable(address.objectId,- toast_options);+ optionsDefListValdateNamespaces(+ ((CreateStmt *) stmt)->options,+ validnsps);++ toastDefList = optionsDefListFilterNamespaces(+ ((CreateStmt *) stmt)->options, "toast");++ toast_options = transformOptions(+ get_toast_relopt_catalog(), (Datum) 0,+ toastDefList, 0);++ success = NewRelationCreateToastTable(+ address.objectId, toast_options);+ if (!success && toast_options)+ ereport(ERROR,+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),+ errmsg("no TOAST relation was created for a table. Can't set toast.* storage parameters")));
}
else if (IsA(stmt, CreateForeignTableStmt))
{
@@ -1101,8 +1109,13 @@ 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);+ if (OidIsValid(relid))+ {+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);+ relid = AlterTableLookupRelation(atstmt, lockmode);+ }
if (OidIsValid(relid))
{
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.cindex da8b42d..e8c4309 100644--- a/src/backend/utils/cache/attoptcache.c+++ b/src/backend/utils/cache/attoptcache.c@@ -149,7 +149,10 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);+ bytea *bytea_opts;++ bytea_opts = optionsTextArrayToBytea(+ get_attribute_options_catalog(), datum);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.cindex b8e3780..cc43f09 100644--- a/src/backend/utils/cache/relcache.c+++ b/src/backend/utils/cache/relcache.c@@ -460,7 +460,8 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
options = extractRelOptions(tuple,
GetPgClassDescriptor(),
relation->rd_rel->relkind == RELKIND_INDEX ?
- relation->rd_amroutine->amoptions : NULL);+ relation->rd_amroutine->amrelopt_catalog : NULL+ );
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.cindex eafc00f..35db485 100644--- a/src/backend/utils/cache/spccache.c+++ b/src/backend/utils/cache/spccache.c@@ -149,7 +149,10 @@ get_tablespace(Oid spcid)
opts = NULL;
else
{
- bytea *bytea_opts = tablespace_reloptions(datum, false);+ bytea *bytea_opts;++ bytea_opts = optionsTextArrayToBytea(+ get_tablespace_options_catalog(), datum);
opts = MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts));
memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
@@ -194,7 +197,6 @@ get_tablespace_page_costs(Oid spcid,
else
*spc_random_page_cost = spc->opts->random_page_cost;
}
-
if (spc_seq_page_cost)
{
if (!spc->opts || spc->opts->seq_page_cost < 0)
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.hindex 0db4fc7..b226dc7 100644--- a/src/include/access/amapi.h+++ b/src/include/access/amapi.h@@ -98,10 +98,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,
@@ -139,6 +135,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* get catalog of reloptions definitions */+typedef void *(*amrelopt_catalog_function) ();+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -202,7 +201,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 */
amvalidate_function amvalidate;
ambeginscan_function ambeginscan;
@@ -212,6 +210,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amrelopt_catalog_function amrelopt_catalog; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.hindex 61a3880..d65ad4e 100644--- a/src/include/access/brin.h+++ b/src/include/access/brin.h@@ -37,6 +37,9 @@ 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) \
((relation)->rd_options ? \
((BrinOptions *) (relation)->rd_options)->pagesPerRange : \
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.hindex 3ed6743..00eccac 100644--- a/src/include/access/brin_internal.h+++ b/src/include/access/brin_internal.h@@ -102,7 +102,6 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *brinoptions(Datum reloptions, bool validate);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.hindex adfdb0c..451b372 100644--- a/src/include/access/gin_private.h+++ b/src/include/access/gin_private.h@@ -84,7 +84,7 @@ typedef struct GinState
/* ginutil.c */
-extern bytea *ginoptions(Datum reloptions, bool validate);+extern Datum ginhandler(PG_FUNCTION_ARGS);
extern void initGinState(GinState *state, Relation index);
extern Buffer GinNewBuffer(Relation index);
extern void GinInitBuffer(Buffer b, uint32 f);
@@ -103,6 +103,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 *gingetreloptcatalog(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.hindex bfef2df..6a9090e 100644--- a/src/include/access/gist_private.h+++ b/src/include/access/gist_private.h@@ -367,6 +367,24 @@ typedef struct GISTBuildBuffers
int rootlevel;
} GISTBuildBuffers;
++/*+ * Definition of items of enum type. Names and codes. To add or modify item+ * edit both lists+ */+#define GIST_OPTION_BUFFERING_VALUE_NAMES { \+ "on", \+ "off", \+ "auto", \+ (const char *) NULL \+}+typedef enum gist_option_buffering_value_numbers+{+ GIST_OPTION_BUFFERING_ON = 0,+ GIST_OPTION_BUFFERING_OFF = 1,+ GIST_OPTION_BUFFERING_AUTO = 2,+} gist_option_buffering_value_numbers;+
/*
* Storage type for GiST's reloptions
*/
@@ -374,7 +392,7 @@ typedef struct GiSTOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
int fillfactor; /* page fill factor in percent (0..100) */
- int bufferingModeOffset; /* use buffering build? */+ int buffering_mode; /* use buffering build? */
} GiSTOptions;
/* gist.c */
@@ -435,7 +453,6 @@ extern bool gistvalidate(Oid opclassoid);
#define GIST_MIN_FILLFACTOR 10
#define GIST_DEFAULT_FILLFACTOR 90
-extern bytea *gistoptions(Datum reloptions, bool validate);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
@@ -485,6 +502,7 @@ extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
Datum *dst, bool *dstisnull);
extern XLogRecPtr gistGetFakeLSN(Relation rel);
+extern void *gistgetreloptcatalog(void);
/* gistvacuum.c */
extern IndexBulkDeleteResult *gistbulkdelete(IndexVacuumInfo *info,
@@ -503,7 +521,6 @@ extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup,
/* gistbuild.c */
extern IndexBuildResult *gistbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
-extern void gistValidateBufferingOption(char *value);
/* gistbuildbuffers.c */
extern GISTBuildBuffers *gistInitBuildBuffers(int pagesPerBuffer, int levelStep,
diff --git a/src/include/access/hash.h b/src/include/access/hash.hindex e3135c1..84f527b 100644--- a/src/include/access/hash.h+++ b/src/include/access/hash.h@@ -271,6 +271,19 @@ typedef struct HashMetaPageData
typedef HashMetaPageData *HashMetaPage;
++typedef struct HashRelOptions+{+ int32 varlena_header_; /* varlena header (do not touch directly!) */+ int fillfactor; /* page fill factor in percent (0..100) */+} HashRelOptions;+++#define HashGetFillFactor(relation) \+ ((relation)->rd_options ? \+ ((HashRelOptions *) (relation)->rd_options)->fillfactor : \+ HASH_DEFAULT_FILLFACTOR)+
/*
* Maximum size of a hash index item (it's okay to have only one per page)
*/
@@ -374,7 +387,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 Datum hash_any(register const unsigned char *k, register int keylen);
@@ -479,4 +491,6 @@ extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
bool bucket_has_garbage,
IndexBulkDeleteCallback callback, void *callback_state);
+extern void *hashgetreloptcatalog(void);+
#endif /* HASH_H */
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.hindex 2d4c36d..1479646 100644--- a/src/include/access/nbtree.h+++ b/src/include/access/nbtree.h@@ -427,6 +427,17 @@ typedef BTScanOpaqueData *BTScanOpaque;
#define SK_BT_DESC (INDOPTION_DESC << SK_BT_INDOPTION_SHIFT)
#define SK_BT_NULLS_FIRST (INDOPTION_NULLS_FIRST << SK_BT_INDOPTION_SHIFT)
+typedef struct BTRelOptions+{+ int32 varlena_header_; /* varlena header (do not touch directly!) */+ int fillfactor; /* page fill factor in percent (0..100) */+} BTRelOptions;++#define BTGetFillFactor(relation) \+ ((relation)->rd_options ? \+ ((BTRelOptions *) (relation)->rd_options)->fillfactor : \+ BTREE_DEFAULT_FILLFACTOR)+
/*
* external entry points for btree, in nbtree.c
*/
@@ -534,7 +545,6 @@ 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 bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.hnew file mode 100644index 0000000..8c1c03c--- /dev/null+++ b/src/include/access/options.h@@ -0,0 +1,225 @@+/*-------------------------------------------------------------------------+ *+ * options.h+ * Core support for relation and tablespace options (pg_class.reloptions+ * and pg_tablespace.spcoptions)+ *+ * Note: the functions dealing with text-array options values declare+ * them as Datum, not ArrayType *, to avoid needing to include array.h+ * into a lot of low-level code.+ *+ *+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ * src/include/access/options.h+ *+ *-------------------------------------------------------------------------+ */+#ifndef OPTIONS_H+#define OPTIONS_H++#include "storage/lock.h"+#include "nodes/pg_list.h"++/* types supported by reloptions */+typedef enum option_type+{+ OPTION_TYPE_BOOL,+ OPTION_TYPE_INT,+ OPTION_TYPE_REAL,+ OPTION_TYPE_ENUM,+ OPTION_TYPE_STRING+} option_type;++typedef enum option_value_status+{+ OPTION_VALUE_STATUS_EMPTY, /* Option was just initialized */+ OPTION_VALUE_STATUS_RAW, /* Option just came from syntax analyzer in+ * has name, and raw (unparsed) value */+ OPTION_VALUE_STATUS_PARSED, /* Option was parsed and has link to catalog+ * entry and proper value */+ OPTION_VALUE_STATUS_FOR_RESET /* This option came from ALTER xxx+ * RESET */+} option_value_status;++/* flags for reloptinon definition */+typedef enum option_definition_flags+{+ OPTION_DEFINITION_FLAG_FORBID_ALTER = (1 << 0), /* Altering this option+ * is forbidden */+ OPTION_DEFINITION_FLAG_IGNORE = (1 << 1), /* Skip this option while+ * parsing. Used for WITH OIDS+ * special case */+ OPTION_DEFINITION_FLAG_REJECT = (1 << 2) /* Option will be rejected+ * when comes from syntax+ * analyzer, but still have+ * default value and offset */+} option_definition_flags;++/* flags that tells reloption parser how to parse*/+typedef enum options_parse_mode+{+ OPTIONS_PARSE_MODE_VALIDATE = (1 << 0),+ OPTIONS_PARSE_MODE_FOR_ALTER = (1 << 1),+ OPTIONS_PARSE_MODE_FOR_RESET = (1 << 2)+} options_parse_mode;+++/* generic struct to hold shared data */+typedef struct option_definition_basic+{+ const char *name; /* must be first (used as list termination+ * marker) */+ const char *desc;+ LOCKMODE lockmode;+ option_definition_flags flags;+ option_type type;+ int struct_offset; /* offset of the value in Bytea representation */+} option_definition_basic;++/* holds a parsed value */+typedef struct option_value+{+ option_definition_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;++/* reloptions records for specific variable types */+typedef struct option_definition_bool+{+ option_definition_basic base;+ bool default_val;+} option_definition_bool;++typedef struct option_definition_int+{+ option_definition_basic base;+ int default_val;+ int min;+ int max;+} option_definition_int;++typedef struct option_definition_real+{+ option_definition_basic base;+ double default_val;+ double min;+ double max;+} option_definition_real;++typedef struct option_definition_enum+{+ option_definition_basic base;+ const char **allowed_values;/* Null terminated array of allowed values for+ * the option */+ int default_val; /* Number of item of allowed_values array */+} option_definition_enum;++/* validation routines for strings */+typedef void (*validate_string_relopt) (char *value);++/*+ * When storing sting reloptions, we shoud deal with special case when+ * option value is not set. For fixed length options, we just copy default+ * option value into the binary structure. For varlen value, there can be+ * "not set" special case, with no default value offered.+ * In this case we will set offset value to -1, so code that use relptions+ * can deal this case. For better readability it was defined as a constant.+ */+#define OPTION_STRING_VALUE_NOT_SET_OFFSET -1++typedef struct option_definition_string+{+ option_definition_basic base;+ validate_string_relopt validate_cb;+ char *default_val;+} option_definition_string;++typedef void (*postprocess_bytea_options_function) (void *data, bool validate);++typedef struct options_catalog+{+ option_definition_basic **definitions;+ int num; /* Number of catalog items in use */+ int num_allocated; /* Number of catalog items allocated */+ bool forbid_realloc; /* If number of items of the catalog were+ * strictly set to certain value+ * do no allow adding more idems */+ Size struct_size; /* Size of a structure for options in binary+ * representation */+ postprocess_bytea_options_function postprocess_fun; /* This function is+ * called after options+ * were converted in+ * Bytea represenation.+ * Can be used for extra+ * validation and so on */+ char *namespace; /* Catalog is used for options from this+ * namespase */+} options_catalog;+++/*+ * Options catalog related functions+ */+extern options_catalog *allocateOptionsCatalog(char *namespace, int size_of_bytea,+ int num_items_expected);+extern void optionsCatalogAddItemBool(options_catalog * catalog, char *name,+ char *desc, LOCKMODE lockmode, option_definition_flags flags,+ int struct_offset, bool default_val);+extern void optionsCatalogAddItemInt(options_catalog * catalog, char *name,+ char *desc, LOCKMODE lockmode, option_definition_flags flags,+ int struct_offset, int default_val, int min_val, int max_val);+extern void optionsCatalogAddItemReal(options_catalog * catalog, char *name,+ char *desc, LOCKMODE lockmode, option_definition_flags flags,+ int struct_offset, double default_val, double min_val, double max_val);+extern void optionsCatalogAddItemEnum(options_catalog * catalog,+ char *name, char *desc, LOCKMODE lockmode, option_definition_flags flags,+ int struct_offset, const char **allowed_values, int default_val);+extern void optionsCatalogAddItemString(options_catalog * catalog, char *name,+ char *desc, LOCKMODE lockmode, option_definition_flags flags,+ int struct_offset, char *default_val, validate_string_relopt validator);+++/*+ * This macro allows to get string option value from bytea representation.+ * "optstruct" - is a structure that is stored in bytea options representation+ * "member" - member of this structure that has string option value+ * (actually string values are stored in bytea after the structure, and+ * and "member" will contain an offset to this value. This macro do all+ * the math+ */+#define GET_STRING_OPTION(optstruct, member) \+ ((optstruct)->member == OPTION_STRING_VALUE_NOT_SET_OFFSET ? NULL : \+ (char *)(optstruct) + (optstruct)->member)++/*+ * Functions related to option convertation, parsing, manipulation+ * and validation+ */+extern void optionsDefListValdateNamespaces(List *defList,+ char **allowed_namespaces);+extern List *optionsDefListFilterNamespaces(List *defList, char *namespace);+extern List *optionsTextArrayToDefList(Datum options);++/*+ * Meta functions that uses functions above to get options for relations,+ * tablespaces, views and so on+ */++extern bytea *optionsTextArrayToBytea(options_catalog * catalog, Datum data);+extern Datum transformOptions(options_catalog * catalog, Datum oldOptions,+ List *defList, options_parse_mode parse_mode);++#endif /* OPTIONS_H */diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.hindex 5cdaa3b..396263f 100644--- a/src/include/access/reloptions.h+++ b/src/include/access/reloptions.h@@ -1,13 +1,9 @@
/*-------------------------------------------------------------------------
*
* reloptions.h
- * Core support for relation and tablespace options (pg_class.reloptions+ * Support for relation 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-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -20,264 +16,27 @@
#define RELOPTIONS_H
#include "access/amapi.h"
-#include "access/htup.h"-#include "access/tupdesc.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_STRING-} relopt_type;+#include "access/options.h"-/* kinds supported by reloptions */-typedef enum relopt_kind-{- 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;- 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;--/* validation routines for strings */-typedef void (*validate_string_relopt) (char *value);--typedef struct relopt_string-{- relopt_gen gen;- int default_len;- bool default_isnull;- validate_string_relopt validate_cb;- char *default_val;-} relopt_string;--/* This is the table datatype for fillRelOptions */-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;---/*- * These macros exist for the convenience of amoptions writers (but consider- * using fillRelOptions, which is a lot simpler). Beware of multiple- * evaluation of arguments!- *- * The last argument in the HANDLE_*_RELOPTION macros allows the caller to- * determine whether the option was set (true), or its value acquired from- * defaults (false); it can be passed as (char *) NULL if the caller does not- * need this information.- *- * optname is the option name (a string), var is the variable- * on which the value should be stored (e.g. StdRdOptions->fillfactor), and- * option is a relopt_value pointer.- *- * The normal way to use this is to loop on the relopt_value array returned by- * parseRelOptions:- * for (i = 0; options[i].gen->name; i++)- * {- * if (HAVE_RELOPTION("fillfactor", options[i])- * {- * HANDLE_INT_RELOPTION("fillfactor", rdopts->fillfactor, options[i], &isset);- * continue;- * }- * if (HAVE_RELOPTION("default_row_acl", options[i])- * {- * ...- * }- * ...- * if (validate)- * ereport(ERROR,- * (errmsg("unknown option")));- * }- *- * Note that this is more or less the same that fillRelOptions does, so only- * use this if you need to do something non-standard within some option's- * code block.- */-#define HAVE_RELOPTION(optname, option) \- (pg_strncasecmp(option.gen->name, optname, option.gen->namelen + 1) == 0)--#define HANDLE_INT_RELOPTION(optname, var, option, wasset) \- do { \- if (option.isset) \- var = option.values.int_val; \- else \- var = ((relopt_int *) option.gen)->default_val; \- (wasset) != NULL ? *(wasset) = option.isset : (dummyret)NULL; \- } while (0)--#define HANDLE_BOOL_RELOPTION(optname, var, option, wasset) \- do { \- if (option.isset) \- var = option.values.bool_val; \- else \- var = ((relopt_bool *) option.gen)->default_val; \- (wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \- } while (0)-#define HANDLE_REAL_RELOPTION(optname, var, option, wasset) \- do { \- if (option.isset) \- var = option.values.real_val; \- else \- var = ((relopt_real *) option.gen)->default_val; \- (wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \- } while (0)--/*- * Note that this assumes that the variable is already allocated at the tail of- * reloptions structure (StdRdOptions or equivalent).- *- * "base" is a pointer to the reloptions structure, and "offset" is an integer- * variable that must be initialized to sizeof(reloptions structure). This- * struct must have been allocated with enough space to hold any string option- * present, including terminating \0 for every option. SET_VARSIZE() must be- * called on the struct with this offset as the second argument, after all the- * string options have been processed.- */-#define HANDLE_STRING_RELOPTION(optname, var, option, base, offset, wasset) \- do { \- relopt_string *optstring = (relopt_string *) option.gen;\- char *string_val; \- if (option.isset) \- string_val = option.values.string_val; \- else if (!optstring->default_isnull) \- string_val = optstring->default_val; \- else \- string_val = NULL; \- (wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \- if (string_val == NULL) \- var = 0; \- else \- { \- strcpy(((char *)(base)) + (offset), string_val); \- var = (offset); \- (offset) += strlen(string_val) + 1; \- } \- } while (0)--/*- * For use during amoptions: get the strlen of a string option- * (either default or the user defined value)- */-#define GET_STRING_RELOPTION_LEN(option) \- ((option).isset ? strlen((option).values.string_val) : \- ((relopt_string *) (option).gen)->default_len)+extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,+ amrelopt_catalog_function catalog_fun);
/*
- * For use by code reading options already parsed: get a pointer to the string- * value itself. "optstruct" is the StdRdOption struct or equivalent, "member"- * is the struct member corresponding to the string option+ * Functions that creates option catalog instances for heap, toast, view+ * and other object types that are part of the postgres core
*/
-#define GET_STRING_RELOPTION(optstruct, member) \- ((optstruct)->member == 0 ? NULL : \- (char *)(optstruct) + (optstruct)->member)+extern options_catalog *get_heap_relopt_catalog(void);+extern options_catalog *get_toast_relopt_catalog(void);-extern relopt_kind add_reloption_kind(void);-extern void add_bool_reloption(bits32 kinds, char *name, char *desc,- bool default_val);-extern void add_int_reloption(bits32 kinds, char *name, char *desc,- int default_val, int min_val, int max_val);-extern void add_real_reloption(bits32 kinds, char *name, char *desc,- double default_val, double min_val, double max_val);-extern void add_string_reloption(bits32 kinds, char *name, char *desc,- char *default_val, validate_string_relopt validator);--extern Datum transformRelOptions(Datum oldOptions, List *defList,- char *namspace, char *validnsps[],- bool ignoreOids, bool isReset);-extern List *untransformRelOptions(Datum options);-extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,- amoptions_function amoptions);-extern relopt_value *parseRelOptions(Datum options, bool validate,- relopt_kind kind, int *numrelopts);-extern void *allocateReloptStruct(Size base, relopt_value *options,- int numoptions);-extern void fillRelOptions(void *rdopts, Size basesize,- relopt_value *options, int numoptions,- bool validate,- const relopt_parse_elt *elems, int nelems);--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 *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);+extern options_catalog *get_view_relopt_catalog(void);+extern options_catalog *get_attribute_options_catalog(void);+extern options_catalog *get_tablespace_options_catalog(void);+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.hindex d1bc396..780e0d7 100644--- a/src/include/access/spgist.h+++ b/src/include/access/spgist.h@@ -20,10 +20,6 @@
#include "lib/stringinfo.h"
-/* reloption parameters */-#define SPGIST_MIN_FILLFACTOR 10-#define SPGIST_DEFAULT_FILLFACTOR 80-
/* SPGiST opclass support function numbers */
#define SPGIST_CONFIG_PROC 1
#define SPGIST_CHOOSE_PROC 2
@@ -181,9 +177,6 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
-/* spgutils.c */-extern bytea *spgoptions(Datum reloptions, bool validate);-
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.hindex 1c4b321..f8c562a 100644--- a/src/include/access/spgist_private.h+++ b/src/include/access/spgist_private.h@@ -21,6 +21,19 @@
#include "utils/relcache.h"
+typedef struct SpGistRelOptions+{+ int32 varlena_header_; /* varlena header (do not touch directly!) */+ int fillfactor; /* page fill factor in percent (0..100) */+} SpGistRelOptions;+++#define SpGistGetFillFactor(relation) \+ ((relation)->rd_options ? \+ ((SpGistRelOptions *) (relation)->rd_options)->fillfactor : \+ SPGIST_DEFAULT_FILLFACTOR)++
/* Page numbers of fixed-location pages */
#define SPGIST_METAPAGE_BLKNO (0) /* metapage */
#define SPGIST_ROOT_BLKNO (1) /* root for normal entries */
@@ -380,6 +393,11 @@ typedef SpGistDeadTupleData *SpGistDeadTuple;
#define GBUF_REQ_NULLS(flags) ((flags) & GBUF_NULLS)
/* spgutils.c */
++/* reloption parameters */+#define SPGIST_MIN_FILLFACTOR 10+#define SPGIST_DEFAULT_FILLFACTOR 80+
extern SpGistCache *spgGetCache(Relation index);
extern void initSpGistState(SpGistState *state, Relation index);
extern Buffer SpGistNewBuffer(Relation index);
@@ -407,6 +425,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page,
Item item, Size size,
OffsetNumber *startOffset,
bool errorOK);
+extern void *spggetreloptcatalog(void);
/* spgdoinsert.c */
extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.hindex f82a799..c649173 100644--- a/src/include/catalog/toasting.h+++ b/src/include/catalog/toasting.h@@ -19,7 +19,7 @@
/*
* toasting.c prototypes
*/
-extern void NewRelationCreateToastTable(Oid relOid, Datum reloptions);+extern bool NewRelationCreateToastTable(Oid relOid, Datum reloptions);
extern void NewHeapCreateToastTable(Oid relOid, Datum reloptions,
LOCKMODE lockmode);
extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.hindex da3ff5d..dc704b1 100644--- a/src/include/commands/tablecmds.h+++ b/src/include/commands/tablecmds.h@@ -32,7 +32,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt);
-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/include/utils/rel.h b/src/include/utils/rel.hindex 4bc61e5..e276d8f 100644--- a/src/include/utils/rel.h+++ b/src/include/utils/rel.h@@ -273,114 +273,123 @@ typedef struct AutoVacOpts
float8 analyze_scale_factor;
} AutoVacOpts;
-typedef struct StdRdOptions+typedef struct HeapOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
int fillfactor; /* page fill factor in percent (0..100) */
AutoVacOpts autovacuum; /* autovacuum-related options */
bool user_catalog_table; /* use as an additional catalog relation */
int parallel_workers; /* max number of parallel workers */
-} StdRdOptions;+} HeapOptions;++typedef struct ToastOptions+{+ int32 vl_len_; /* varlena header (do not touch directly!) */+ AutoVacOpts autovacuum; /* autovacuum-related options */+} ToastOptions;
#define HEAP_MIN_FILLFACTOR 10
#define HEAP_DEFAULT_FILLFACTOR 100
+#define TOAST_DEFAULT_FILLFACTOR 100 /* Only default is actually used */+
/*
- * RelationGetFillFactor+ * HeapGetFillFactor
* Returns the relation's fillfactor. Note multiple eval of argument!
*/
-#define RelationGetFillFactor(relation, defaultff) \+#define HeapGetFillFactor(relation, defaultff) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff))+ ((HeapOptions *) (relation)->rd_options)->fillfactor : (defaultff))
/*
- * RelationGetTargetPageUsage- * Returns the relation's desired space usage per page in bytes.- */-#define RelationGetTargetPageUsage(relation, defaultff) \- (BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100)--/*- * RelationGetTargetPageFreeSpace+ * HeapGetTargetPageFreeSpace
* Returns the relation's desired freespace per page in bytes.
*/
-#define RelationGetTargetPageFreeSpace(relation, defaultff) \- (BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)+#define HeapGetTargetPageFreeSpace(relation, defaultff) \+ (BLCKSZ * (100 - HeapGetFillFactor(relation, defaultff)) / 100)
/*
- * RelationIsUsedAsCatalogTable+ * HeapIsUsedAsCatalogTable
* Returns whether the relation should be treated as a catalog table
* from the pov of logical decoding. Note multiple eval of argument!
*/
-#define RelationIsUsedAsCatalogTable(relation) \+#define HeapIsUsedAsCatalogTable(relation) \
((relation)->rd_options && \
((relation)->rd_rel->relkind == RELKIND_RELATION || \
(relation)->rd_rel->relkind == RELKIND_MATVIEW) ? \
- ((StdRdOptions *) (relation)->rd_options)->user_catalog_table : false)+ ((HeapOptions *) (relation)->rd_options)->user_catalog_table : false)
/*
- * RelationGetParallelWorkers+ * HeapGetParallelWorkers
* Returns the relation's parallel_workers reloption setting.
* Note multiple eval of argument!
*/
-#define RelationGetParallelWorkers(relation, defaultpw) \+#define HeapGetParallelWorkers(relation, defaultpw) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))+ ((HeapOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))+#define ToastGetTargetPageFreeSpace() \+ (BLCKSZ * (100 - TOAST_DEFAULT_FILLFACTOR) / 100)
/*
* ViewOptions
* Contents of rd_options for views
*/
++/*+ * Definition of items of enum type. Names and codes. To add or modify item+ * edit both lists+ */+#define VIEW_OPTION_CHECK_OPTION_VALUE_NAMES { \+ "local", \+ "cascaded", \+ (const char *) NULL \+}++typedef enum view_option_check_option_value_numbers+{+ VIEW_OPTION_CHECK_OPTION_NOT_SET = -1,+ VIEW_OPTION_CHECK_OPTION_LOCAL = 0,+ VIEW_OPTION_CHECK_OPTION_CASCADED = 1,+} view_option_check_option_value_numbers;+
typedef struct ViewOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
bool security_barrier;
- int check_option_offset;+ int check_option;
} ViewOptions;
/*
- * RelationIsSecurityView- * Returns whether the relation is security view, or not. Note multiple- * eval of argument!+ * ViewIsSecurityView+ * Returns whether the view is security view, or not. Note multiple eval+ * of argument!
*/
-#define RelationIsSecurityView(relation) \+#define ViewIsSecurityView(relation) \
((relation)->rd_options ? \
- ((ViewOptions *) (relation)->rd_options)->security_barrier : false)+ ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
/*
- * RelationHasCheckOption- * Returns true if the relation is a view defined with either the local- * or the cascaded check option. Note multiple eval of argument!+ * ViewHasCheckOption+ * Returns true if view is defined with either the local or the cascaded+ * check option. Note multiple eval of argument!
*/
-#define RelationHasCheckOption(relation) \- ((relation)->rd_options && \- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0)-/*- * RelationHasLocalCheckOption- * Returns true if the relation is a view defined with the local check- * option. Note multiple eval of argument!- */-#define RelationHasLocalCheckOption(relation) \- ((relation)->rd_options && \- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \- strcmp((char *) (relation)->rd_options + \- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \- "local") == 0 : false)+#define ViewHasCheckOption(relation) \+((relation)->rd_options && \+ ((ViewOptions *) (relation)->rd_options)->check_option != \+ VIEW_OPTION_CHECK_OPTION_NOT_SET)
/*
- * RelationHasCascadedCheckOption- * Returns true if the relation is a view defined with the cascaded check- * option. Note multiple eval of argument!+ * ViewHasCascadedCheckOption+ * Returns true if the view is defined with the cascaded check option.+ * Note multiple eval of argument!
*/
-#define RelationHasCascadedCheckOption(relation) \- ((relation)->rd_options && \- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \- strcmp((char *) (relation)->rd_options + \- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \- "cascaded") == 0 : false)+#define ViewHasCascadedCheckOption(relation) \+((relation)->rd_options && \+ ((ViewOptions *) (relation)->rd_options)->check_option == \+ VIEW_OPTION_CHECK_OPTION_CASCADED)
/*
* RelationIsValid
@@ -559,7 +568,7 @@ typedef struct ViewOptions
#define RelationIsAccessibleInLogicalDecoding(relation) \
(XLogLogicalInfoActive() && \
RelationNeedsWAL(relation) && \
- (IsCatalogRelation(relation) || RelationIsUsedAsCatalogTable(relation)))+ (IsCatalogRelation(relation) || HeapIsUsedAsCatalogTable(relation)))
/*
* RelationIsLogicallyLogged
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.outindex 8385887..4f79224 100644--- a/src/test/regress/expected/alter_table.out+++ b/src/test/regress/expected/alter_table.out@@ -2378,7 +2378,9 @@ ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks set (autovacuum_enabled = false);
ERROR: unrecognized parameter "autovacuum_enabled"
alter table my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
begin;
alter view my_locks set (security_barrier=off);
select * from my_locks order by 1;
diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.outindex ca80f00..6e9c4a2 100644--- a/src/test/regress/expected/brin.out+++ b/src/test/regress/expected/brin.out@@ -507,3 +507,8 @@ EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE b = 1;
Filter: (b = 1)
(2 rows)
+-- Check that changing reloptions for brin index is not allowed+ALTER INDEX brinidx SET (pages_per_range = 10);+ERROR: changing parameter "pages_per_range" is not allowed+ALTER INDEX brinidx RESET (pages_per_range);+ERROR: changing parameter "pages_per_range" is not alloweddiff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.outindex f5a2993..b7550b1 100644--- a/src/test/regress/expected/gist.out+++ b/src/test/regress/expected/gist.out@@ -13,7 +13,7 @@ drop index gist_pointidx2, gist_pointidx3, gist_pointidx4;
-- Make sure bad values are refused
create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
ERROR: invalid value for "buffering" option
-DETAIL: Valid values are "on", "off", and "auto".+DETAIL: Valid values are "on", "off", "auto".
create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
ERROR: value 9 out of bounds for option "fillfactor"
DETAIL: Valid values are between "10" and "100".
@@ -27,6 +27,20 @@ insert into gist_point_tbl (id, p)
select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);+--Test buffering and fillfactor for refising invalid values+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);+ERROR: invalid value for "buffering" option+DETAIL: Valid values are "on", "off", "auto".+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);+ERROR: value 9 out of bounds for option "fillfactor"+DETAIL: Valid values are between "10" and "100".+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);+ERROR: value 101 out of bounds for option "fillfactor"+DETAIL: Valid values are between "10" and "100".
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
@@ -34,6 +48,7 @@ delete from gist_point_tbl where id < 10000;
vacuum analyze gist_point_tbl;
-- rebuild the index with a different fillfactor
alter index gist_pointidx SET (fillfactor = 40);
+ERROR: changing parameter "fillfactor" is not allowed
reindex index gist_pointidx;
--
-- Test Index-only plans on GiST indexes
diff --git a/src/test/regress/expected/hash_index.out b/src/test/regress/expected/hash_index.outindex e23de21..cba7665 100644--- a/src/test/regress/expected/hash_index.out+++ b/src/test/regress/expected/hash_index.out@@ -219,6 +219,7 @@ INSERT INTO hash_split_heap SELECT a/2 FROM generate_series(1, 25000) a;
VACUUM hash_split_heap;
-- Rebuild the index using a different fillfactor
ALTER INDEX hash_split_index SET (fillfactor = 10);
+ERROR: changing parameter "fillfactor" is not allowed
REINDEX INDEX hash_split_index;
-- Clean up.
DROP TABLE hash_split_heap;
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.outindex c4107d5..231c049 100644--- a/src/test/regress/expected/reloptions.out+++ b/src/test/regress/expected/reloptions.out@@ -77,7 +77,7 @@ SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
ALTER TABLE reloptions_test RESET (autovacuum_enabled,
autovacuum_analyze_scale_factor);
SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND
-reloptions IS NULL;+ reloptions IS NULL;
reloptions
------------
@@ -86,7 +86,7 @@ reloptions IS NULL;
-- RESET fails if a value is specified
ALTER TABLE reloptions_test RESET (fillfactor=12);
ERROR: RESET must not include values for parameters
--- The OIDS option is not stored+-- The OIDS option is not stored as reloption
DROP TABLE reloptions_test;
CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
SELECT reloptions, relhasoids FROM pg_class WHERE oid = 'reloptions_test'::regclass;
@@ -123,7 +123,24 @@ 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"+-- Fail on setting reloption to a table that does not have a TOAST relation+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );+ERROR: no TOAST relation was created for a table. Can't set toast.* storage parameters+DROP TABLE reloptions_test;+CREATE TABLE reloptions_test(i INT);+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be+-- accepted by heap but rejected by toast (special case)+DROP TABLE reloptions_test;+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);+ERROR: unrecognized parameter "toast.autovacuum_analyze_scale_factor"+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);+ERROR: unrecognized parameter "toast.autovacuum_analyze_threshold"
-- Mix TOAST & heap
DROP TABLE reloptions_test;
CREATE TABLE reloptions_test (s VARCHAR) WITH
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.outindex 2090a41..55b216c 100644--- a/src/test/regress/expected/updatable_views.out+++ b/src/test/regress/expected/updatable_views.out@@ -1551,7 +1551,7 @@ SELECT * FROM base_tbl;
ALTER VIEW rw_view1 SET (check_option=here); -- invalid
ERROR: invalid value for "check_option" option
-DETAIL: Valid values are "local" and "cascaded".+DETAIL: Valid values are "local", "cascaded".
ALTER VIEW rw_view1 SET (check_option=local);
INSERT INTO rw_view2 VALUES (-20); -- should fail
ERROR: new row violates check option for view "rw_view1"
diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.sqlindex 11f8fe9..3559ef6 100644--- a/src/test/regress/sql/brin.sql+++ b/src/test/regress/sql/brin.sql@@ -460,3 +460,6 @@ VACUUM ANALYZE brin_test;
EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE a = 1;
-- Ensure brin index is not used when values are not correlated
EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE b = 1;
+-- Check that changing reloptions for brin index is not allowed+ALTER INDEX brinidx SET (pages_per_range = 10);+ALTER INDEX brinidx RESET (pages_per_range);diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.sqlindex bae722f..9e01909 100644--- a/src/test/regress/sql/gist.sql+++ b/src/test/regress/sql/gist.sql@@ -28,6 +28,15 @@ select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);+--Test buffering and fillfactor for refising invalid values+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);+
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sqlindex c9119fd..21a315b 100644--- a/src/test/regress/sql/reloptions.sql+++ b/src/test/regress/sql/reloptions.sql@@ -47,12 +47,12 @@ SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
ALTER TABLE reloptions_test RESET (autovacuum_enabled,
autovacuum_analyze_scale_factor);
SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND
-reloptions IS NULL;+ reloptions IS NULL;
-- RESET fails if a value is specified
ALTER TABLE reloptions_test RESET (fillfactor=12);
--- The OIDS option is not stored+-- The OIDS option is not stored as reloption
DROP TABLE reloptions_test;
CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
SELECT reloptions, relhasoids FROM pg_class WHERE oid = 'reloptions_test'::regclass;
@@ -75,6 +75,22 @@ 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);
+-- Fail on setting reloption to a table that does not have a TOAST relation+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );+DROP TABLE reloptions_test;++CREATE TABLE reloptions_test(i INT);+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);++-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be+-- accepted by heap but rejected by toast (special case)+DROP TABLE reloptions_test;+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);++CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);+
-- Mix TOAST & heap
DROP TABLE reloptions_test;
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: dhyan@nataraj.su, alvherre@alvh.no-ip.org, andres@anarazel.de
Subject: Re: [PATCH] Move all am-related reloption code into src/backend/access/[am-name] and get rid of relopt_kind for custom AM
In-Reply-To: <3148424.k4fWhFzS7G@x200m>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox