From: Nikolay Shaplov <dhyan@nataraj.su>
To: pgsql-hackers@postgresql.org
Cc: Andres Freund <andres@anarazel.de>
Cc: Alvaro Herrera <alvherre@2ndquadrant.com>
Subject: Re: [PATCH v.7a] Move all am-related reloption code into src/backend/access/[am-name] and get rid of relopt_kind for custom AM
Date: Wed, 19 Apr 2017 21:31:42 +0300
Message-ID: <2911117.zAfR6qRW1g@x200m> (raw)
In-Reply-To: <20170403183128.cqzycjjqc63glgqf@alap3.anarazel.de>
References: <20170317172126.o452q2cjds6o3pfx@alvherre.pgsql>
<1512589.SLJRFdfOaX@x200m>
<20170403183128.cqzycjjqc63glgqf@alap3.anarazel.de>
List-Unsubscribe: <mailto:majordomo@postgresql.org?body=unsub%20pgsql-hackers>
В письме от 3 апреля 2017 11:31:28 пользователь Andres Freund написал:
> > Here is a new version of the patch.
>
> Thanks for the new version of the patch. As this commitfest is running
> out of time, and as the patch has been submitted late in the v10
> development cycle, I've moved this patch to the next commitfest.
Alas...
But I am quite patient, so here is the same patch reabesed on current master,
And it also includes all changes that have been done to reloptions since
previous one. (autosummarize for brin and no options for partitioned tables)
--
Nikolay Shaplov, independent Perl & C/C++ developer. Available for hire.=
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackersAttachments:reloptions7a.diff (text/x-patch, 220.8K) downloaddiff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.hindex 0cfe49a..96edc9a 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 cbc50f7..7e3bbc0 100644--- a/contrib/bloom/expected/bloom.out+++ b/contrib/bloom/expected/bloom.out@@ -210,3 +210,37 @@ ORDER BY 1;
text_ops | t
(2 rows)
+-- reloptions test+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;+ reloptions +-------------------+ {length=7,col1=4}+(1 row)++-- check for min and max values+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 2227460..dbac21c 100644--- a/contrib/bloom/sql/bloom.sql+++ b/contrib/bloom/sql/bloom.sql@@ -81,3 +81,23 @@ SELECT opcname, amvalidate(opc.oid)
FROM pg_opclass opc JOIN pg_am am ON am.oid = opcmethod
WHERE amname = 'bloom'
ORDER BY 1;
++-- reloptions test++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+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 44b67da..0865dd9 100644--- a/contrib/dblink/dblink.c+++ b/contrib/dblink/dblink.c@@ -1955,7 +1955,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 277639f..876b8bb 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 e24db56..be15189 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 2594407..0555919 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;
@@ -797,37 +798,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.
*/
@@ -1424,3 +1394,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 fb27944..c9fb063 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 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 6d1f22f..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 d03d59d..a942738 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;
@@ -593,35 +594,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
*
@@ -698,3 +670,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 6593771..c366c77 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 f1f08bb..09d0c68 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 cbdaec9..fddb863 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.
*/
@@ -812,34 +811,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.
*
@@ -965,3 +936,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 3eb5b1d..2091dbd 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 3cd4daa..bf87861 100644--- a/src/backend/access/hash/hashpage.c+++ b/src/backend/access/hash/hashpage.c@@ -360,7 +360,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 9f832f2..131c5d1 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
*/
@@ -580,3 +574,28 @@ _hash_kill_items(IndexScanDesc scan)
MarkBufferDirtyHint(so->hashso_curbuf, true);
}
}
++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 0c3e2b0..1d0e762 100644--- a/src/backend/access/heap/heapam.c+++ b/src/backend/access/heap/heapam.c@@ -2670,7 +2670,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 6529fe3..6668bd2 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 d69a266..9735ec7 100644--- a/src/backend/access/heap/pruneheap.c+++ b/src/backend/access/heap/pruneheap.c@@ -131,7 +131,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 60dcb67..6bd6df0 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 6dca810..6b12304 100644--- a/src/backend/access/nbtree/nbtinsert.c+++ b/src/backend/access/nbtree/nbtinsert.c@@ -1433,8 +1433,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 775f2ff..27d274e 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
{
@@ -116,8 +116,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.
@@ -150,7 +149,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;
@@ -160,6 +158,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;
@@ -1391,3 +1390,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 3d041c4..92ce827 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 5b259a3..867d3e6 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 e57ac49..a6416d7 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 29756eb..aaa9270 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 06425cc..52f4c49 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 96cf296..65652f9 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 4861799..48be80b 100644--- a/src/backend/commands/indexcmds.c+++ b/src/backend/commands/indexcmds.c@@ -325,7 +325,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;
@@ -523,7 +523,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;+ amoption_catalog = amRoutine->amrelopt_catalog;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -537,10 +537,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 a02904c..23e6716 100644--- a/src/backend/commands/tablecmds.c+++ b/src/backend/commands/tablecmds.c@@ -505,7 +505,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
@@ -591,13 +590,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)
{
@@ -3117,7 +3126,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);
@@ -3159,13 +3169,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)
{
@@ -3377,7 +3389,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:
@@ -4155,7 +4170,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",
@@ -6250,11 +6265,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));
@@ -10256,7 +10271,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 */
@@ -10285,25 +10300,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,
@@ -10317,7 +10359,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;
@@ -10377,6 +10419,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);
@@ -10400,12 +10444,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));
@@ -10433,6 +10480,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 f9c2620..5a60122 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 9207c8d..ac176c0 100644--- a/src/backend/optimizer/util/plancat.c+++ b/src/backend/optimizer/util/plancat.c@@ -140,7 +140,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 e268a12..ac7d6be 100644--- a/src/backend/parser/parse_clause.c+++ b/src/backend/parser/parse_clause.c@@ -3124,7 +3124,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 e546194..27cfa7e 100644--- a/src/backend/parser/parse_utilcmd.c+++ b/src/backend/parser/parse_utilcmd.c@@ -1509,7 +1509,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 89dd3b3..a92d6f0 100644--- a/src/backend/postmaster/autovacuum.c+++ b/src/backend/postmaster/autovacuum.c@@ -2735,6 +2735,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 ||
@@ -2744,8 +2745,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 4dcb713..f26cc54 100644--- a/src/backend/rewrite/rewriteHandler.c+++ b/src/backend/rewrite/rewriteHandler.c@@ -1531,7 +1531,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 */
@@ -3032,7 +3032,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
@@ -3068,8 +3068,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 24e5c42..8639803 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,++ optionsDefListValdateNamespaces(
((CreateStmt *) stmt)->options,
- "toast",- validnsps,- true,- false);- (void) heap_reloptions(RELKIND_TOASTVALUE,- toast_options,- true);+ validnsps);++ toastDefList = optionsDefListFilterNamespaces(+ ((CreateStmt *) stmt)->options, "toast");- NewRelationCreateToastTable(address.objectId,- toast_options);+ 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))
{
@@ -1610,6 +1623,7 @@ ProcessUtilitySlow(ParseState *pstate,
case T_AlterPublicationStmt:
AlterPublication((AlterPublicationStmt *) parsetree);
+
/*
* AlterPublication calls EventTriggerCollectSimpleCommand
* directly
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.cindex f7f85b5..d20b680 100644--- a/src/backend/utils/cache/attoptcache.c+++ b/src/backend/utils/cache/attoptcache.c@@ -150,7 +150,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 85c6b61..d5d8353 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 f919cf8..9f3db93 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 e03aa08..0f3d4e4 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 abe8877..4c32dd1 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 986fe6e..6b869f2 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 1ad4ed6..6a7c2cb 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 adba224..44e2ac4 100644--- a/src/include/access/hash.h+++ b/src/include/access/hash.h@@ -224,6 +224,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)
*/
@@ -319,7 +332,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);
@@ -422,4 +434,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 15771ce..28d9314 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 91b2cd7..86dff99 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 9dca8fd..9c57eba 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
@@ -182,9 +178,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 4072c05..d99adc9 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 00d0a83..18cff15 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 f3a9701..33f5782 100644--- a/src/include/commands/tablecmds.h+++ b/src/include/commands/tablecmds.h@@ -31,7 +31,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 ab875bb..47f9aa4 100644--- a/src/include/utils/rel.h+++ b/src/include/utils/rel.h@@ -273,7 +273,7 @@ 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) */
@@ -281,107 +281,116 @@ typedef struct StdRdOptions
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
@@ -560,7 +569,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 883a5c9..072783e 100644--- a/src/test/regress/expected/alter_table.out+++ b/src/test/regress/expected/alter_table.out@@ -2285,7 +2285,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;
@@ -3378,3 +3380,10 @@ create table parted_validate_test_1 partition of parted_validate_test for values
alter table parted_validate_test add constraint parted_validate_test_chka check (a > 0) not valid;
alter table parted_validate_test validate constraint parted_validate_test_chka;
drop table parted_validate_test;
+-- test alter column options+CREATE TABLE tmp(i integer);+INSERT INTO tmp VALUES (1);+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);+ANALYZE tmp;+DROP TABLE tmp;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/create_index.out b/src/test/regress/expected/create_index.outindex 26cd059..7b8f805 100644--- a/src/test/regress/expected/create_index.out+++ b/src/test/regress/expected/create_index.out@@ -2337,7 +2337,7 @@ Options: fastupdate=on, gin_pending_list_limit=128
CREATE INDEX hash_i4_index ON hash_i4_heap USING hash (random int4_ops);
CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
DROP TABLE unlogged_hash_table;
diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.outindex c7181b0..19f1881 100644--- a/src/test/regress/expected/gist.out+++ b/src/test/regress/expected/gist.out@@ -12,6 +12,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)
diff --git a/src/test/regress/expected/hash_index.out b/src/test/regress/expected/hash_index.outindex 0bbaa2a..6d5cf0d 100644--- a/src/test/regress/expected/hash_index.out+++ b/src/test/regress/expected/hash_index.out@@ -229,3 +229,12 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
DROP TABLE hash_heap_float4 CASCADE;
+-- Check reloptions min max values and that it is not allowed to ALTER+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);+ERROR: value 9 out of bounds for option "fillfactor"+DETAIL: Valid values are between "10" and "100".+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);+ERROR: value 101 out of bounds for option "fillfactor"+DETAIL: Valid values are between "10" and "100".+ALTER INDEX hash_f8_index SET (fillfactor=99);+ERROR: changing parameter "fillfactor" is not alloweddiff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.outnew file mode 100644index 0000000..835d72f--- /dev/null+++ b/src/test/regress/expected/reloptions.out@@ -0,0 +1,193 @@+-- Simple create+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;+ reloptions +------------------------------------------------------------------------------+ {fillfactor=30,autovacuum_enabled=false,autovacuum_analyze_scale_factor=0.2}+(1 row)++-- Test сase insensitive+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;+ reloptions +-----------------+ {fillfactor=40}+(1 row)++-- Fail on min/max values check+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);+ERROR: value 2 out of bounds for option "fillfactor"+DETAIL: Valid values are between "10" and "100".+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);+ERROR: value 110 out of bounds for option "fillfactor"+DETAIL: Valid values are between "10" and "100".+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);+ERROR: value -10.0 out of bounds for option "autovacuum_analyze_scale_factor"+DETAIL: Valid values are between "0.000000" and "100.000000".+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);+ERROR: value 110.0 out of bounds for option "autovacuum_analyze_scale_factor"+DETAIL: Valid values are between "0.000000" and "100.000000".+-- Fail when option and namespase do not exist+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);+ERROR: unrecognized parameter "not_existing_option"+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);+ERROR: unrecognized parameter namespace "not_existing_namespace"+-- Fail while setting unproper value+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);+ERROR: invalid value for integer option "fillfactor": 30.5+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');+ERROR: invalid value for integer option "fillfactor": string+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);+ERROR: invalid value for integer option "fillfactor": true+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);+ERROR: invalid value for boolean option "autovacuum_enabled": 12+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);+ERROR: invalid value for boolean option "autovacuum_enabled": 30.5+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');+ERROR: invalid value for boolean option "autovacuum_enabled": string+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": string+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": true+-- Specifing name only should fail as if there was =ture after it+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor); +ERROR: invalid value for integer option "fillfactor": true+-- Simple ALTER TABLE+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;+ reloptions +------------------------------------------------------------------------------+ {autovacuum_enabled=false,fillfactor=31,autovacuum_analyze_scale_factor=0.3}+(1 row)++-- Check that we cat set boolean option to true just by mentioning it+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;+ reloptions +-----------------------------------------------------------------------------+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true,fillfactor=32}+(1 row)++-- Check that RESET works well+ALTER TABLE reloptions_test RESET (fillfactor);+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;+ reloptions +---------------------------------------------------------------+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true}+(1 row)++-- Check that RESETting all values make NULL reloptions record in pg_class+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 +------------+ +(1 row)++-- Check RESET fails on att=value+ALTER TABLE reloptions_test RESET (fillfactor=12);+ERROR: RESET must not include values for parameters+-- Check oids options is ignored+DROP TABLE reloptions_test;+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;+ reloptions +-----------------+ {fillfactor=20}+(1 row)++-- Now testing toast.* options+DROP TABLE reloptions_test;+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);+ reloptions +-----------------------------------+ {autovacuum_vacuum_cost_delay=23}+(1 row)++ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);+ reloptions +-----------------------------------+ {autovacuum_vacuum_cost_delay=24}+(1 row)++ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);+ reloptions +------------+ +(1 row)++-- Fail on unexisting options in toast namespace+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );+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"+-- And now mixed toast + heap+DROP TABLE reloptions_test;+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;+ reloptions +-------------------------------------------------+ {autovacuum_vacuum_cost_delay=24,fillfactor=40}+(1 row)++SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);+ reloptions +-----------------------------------+ {autovacuum_vacuum_cost_delay=23}+(1 row)++-- Same FOR CREATE and ALTER INDEX for btree indexes+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;+ reloptions +-----------------+ {fillfactor=30}+(1 row)++-- Fail when option and namespase do not exist+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);+ERROR: unrecognized parameter "not_existing_option"+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);+ERROR: unrecognized parameter namespace "not_existing_option"+-- Check ranges+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);+ERROR: value 1 out of bounds for option "fillfactor"+DETAIL: Valid values are between "10" and "100".+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);+ERROR: value 130 out of bounds for option "fillfactor"+DETAIL: Valid values are between "10" and "100".+-- Check alter+ALTER INDEX reloptions_test_idx SET (fillfactor=40);+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;+ reloptions +-----------------+ {fillfactor=40}+(1 row)++-- Check alter on empty relop list+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;+ reloptions +-----------------+ {fillfactor=40}+(1 row)+diff --git a/src/test/regress/expected/spgist.out b/src/test/regress/expected/spgist.outindex 0691e91..bb58fb7 100644--- a/src/test/regress/expected/spgist.out+++ b/src/test/regress/expected/spgist.out@@ -4,7 +4,7 @@
-- There are other tests to test different SP-GiST opclasses. This is for
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
insert into spgist_point_tbl (id, p)
@@ -37,3 +37,12 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+-- Check reloptions min, max values+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);+ERROR: value 9 out of bounds for option "fillfactor"+DETAIL: Valid values are between "10" and "100".+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);+ERROR: value 101 out of bounds for option "fillfactor"+DETAIL: Valid values are between "10" and "100".+-- ALTER reloption should work+alter index spgist_point_idx set (fillfactor = 76);diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.outindex aa06d1d..287dd84 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/input/tablespace.source b/src/test/regress/input/tablespace.sourceindex 03a62bd..7f7934b 100644--- a/src/test/regress/input/tablespace.source+++ b/src/test/regress/input/tablespace.source@@ -12,10 +12,10 @@ DROP TABLESPACE regress_tblspacewith;
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.sourceindex aaedf5f..2443511 100644--- a/src/test/regress/output/tablespace.source+++ b/src/test/regress/output/tablespace.source@@ -14,12 +14,12 @@ DROP TABLESPACE regress_tblspacewith;
-- create a tablespace we can use
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ERROR: unrecognized parameter "some_nonexistent_parameter"
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
ERROR: RESET must not include values for parameters
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
-- try a table
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_scheduleindex 1f8f098..e9b9b33 100644--- a/src/test/regress/parallel_schedule+++ b/src/test/regress/parallel_schedule@@ -109,7 +109,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml reloptions
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_scheduleindex 04206c3..f9c08b7 100644--- a/src/test/regress/serial_schedule+++ b/src/test/regress/serial_schedule@@ -177,5 +177,6 @@ test: returning
test: largeobject
test: with
test: xml
+test: reloptions
test: event_trigger
test: stats
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sqlindex eb1b4b5..33a8741 100644--- a/src/test/regress/sql/alter_table.sql+++ b/src/test/regress/sql/alter_table.sql@@ -2235,3 +2235,10 @@ create table parted_validate_test_1 partition of parted_validate_test for values
alter table parted_validate_test add constraint parted_validate_test_chka check (a > 0) not valid;
alter table parted_validate_test validate constraint parted_validate_test_chka;
drop table parted_validate_test;
+-- test alter column options+CREATE TABLE tmp(i integer);+INSERT INTO tmp VALUES (1);+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);+ANALYZE tmp;+DROP TABLE tmp;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/create_index.sql b/src/test/regress/sql/create_index.sqlindex 1648072..1f8d101 100644--- a/src/test/regress/sql/create_index.sql+++ b/src/test/regress/sql/create_index.sql@@ -682,7 +682,7 @@ CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.sqlindex 9d4ff1e..ce1ac1a 100644--- a/src/test/regress/sql/gist.sql+++ b/src/test/regress/sql/gist.sql@@ -17,6 +17,17 @@ 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/hash_index.sql b/src/test/regress/sql/hash_index.sqlindex 9af03d2..f245525 100644--- a/src/test/regress/sql/hash_index.sql+++ b/src/test/regress/sql/hash_index.sql@@ -192,3 +192,8 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
DROP TABLE hash_heap_float4 CASCADE;
++-- Check reloptions min max values and that it is not allowed to ALTER+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);+ALTER INDEX hash_f8_index SET (fillfactor=99);
\ No newline at end of file
diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sqlnew file mode 100644index 0000000..0bfec28--- /dev/null+++ b/src/test/regress/sql/reloptions.sql@@ -0,0 +1,153 @@++-- Simple create+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);++SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;++-- Test сase insensitive+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);++SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;++-- Fail on min/max values check+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);++CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);++CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);++CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);++-- Fail when option and namespase do not exist++CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);++CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);++-- Fail while setting unproper value++CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);++CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');++CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);++CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);++CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);++CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');++CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');++CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);++-- Specifing name only should fail as if there was =ture after it+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor); ++-- Simple ALTER TABLE++ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);++SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;++-- Check that we cat set boolean option to true just by mentioning it++ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);++SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;++-- Check that RESET works well++ALTER TABLE reloptions_test RESET (fillfactor);++SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;++-- Check that RESETting all values make NULL reloptions record in pg_class+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;++-- Check RESET fails on att=value++ALTER TABLE reloptions_test RESET (fillfactor=12);++-- Check oids options is ignored+DROP TABLE reloptions_test;+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;++-- Now testing toast.* options+DROP TABLE reloptions_test;++CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );++SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);++ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);++SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);++ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);++SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);++-- Fail on unexisting 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);++-- And now mixed toast + heap+DROP TABLE reloptions_test;++CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);++SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);++-- Same FOR CREATE and ALTER INDEX for btree indexes++CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);++SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;++-- Fail when option and namespase do not exist++CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);++CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);++-- Check ranges++CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);++CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);++-- Check alter++ALTER INDEX reloptions_test_idx SET (fillfactor=40);++SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;++-- Check alter on empty relop list++CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);++ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);++SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;++diff --git a/src/test/regress/sql/spgist.sql b/src/test/regress/sql/spgist.sqlindex 5896b50..8d26e49 100644--- a/src/test/regress/sql/spgist.sql+++ b/src/test/regress/sql/spgist.sql@@ -5,7 +5,7 @@
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
@@ -48,3 +48,10 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
++-- Check reloptions min, max values+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);++-- ALTER reloption should work+alter index spgist_point_idx set (fillfactor = 76);
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: dhyan@nataraj.su, andres@anarazel.de, alvherre@2ndquadrant.com
Subject: Re: [PATCH v.7a] Move all am-related reloption code into src/backend/access/[am-name] and get rid of relopt_kind for custom AM
In-Reply-To: <2911117.zAfR6qRW1g@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