agora inbox for [email protected]
help / color / mirror / Atom feedRe: [PATCH] Move all am-related reloption code into src/backend/access/[am-name] and get rid of relopt_kind for custom AM
5+ messages / 4 participants
[nested] [flat]
* Re: [PATCH] Move all am-related reloption code into src/backend/access/[am-name] and get rid of relopt_kind for custom AM
@ 2017-10-30 09:49 Nikolay Shaplov <[email protected]>
0 siblings, 0 replies; 5+ messages in thread
From: Nikolay Shaplov @ 2017-10-30 09:49 UTC (permalink / raw)
To: Alvaro Herrera <[email protected]>; +Cc: pgsql-hackers; Andres Freund <[email protected]>
В письме от 3 сентября 2017 11:45:43 пользователь Alvaro Herrera написал:
Yet another git rebase. This patch can be applied to master branch again.
For this patch no review needed now, as I will offer part of it (one that
refuses to set toast reloptions when there is no TOAST table) as a separate
patch.
--
Do code for fun. Can do it for money (Perl & C/C++ ~10h/week)
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
[text/x-patch] reloptions8.diff (200.2K, ../../3148424.k4fWhFzS7G@x200m/2-reloptions8.diff)
download | inline diff:
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index f3df1af..9c27326 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -204,7 +204,6 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 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.out
index 5ab9e34..732f8b5 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -222,8 +222,27 @@ SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
(1 row)
-- check for min and max values
-\set VERBOSITY terse
CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
ERROR: value 0 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+ERROR: value 4097 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
ERROR: value 0 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+ERROR: value 4096 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+ERROR: col0 should not be grater than length
+-- check that ALTERing of reloptions is forbidden
+ALTER INDEX bloomidx SET (length=4);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx SET (col1=4);
+ERROR: changing parameter "col1" is not allowed
+ALTER INDEX bloomidx RESET (length);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx RESET (col1);
+ERROR: changing parameter "col1" is not allowed
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 32755f2..f164e10 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -89,6 +89,16 @@ DROP INDEX bloomidx;
CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1=4);
SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
-- check for min and max values
-\set VERBOSITY terse
CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+
+-- check that ALTERing of reloptions is forbidden
+
+ALTER INDEX bloomidx SET (length=4);
+ALTER INDEX bloomidx SET (col1=4);
+ALTER INDEX bloomidx RESET (length);
+ALTER INDEX bloomidx RESET (col1);
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 7dc7716..c4a9e29 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1960,7 +1960,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 94e50e9..b38c15c 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -193,7 +193,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 67e1c59..3fd44bc 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -65,7 +65,7 @@ PG_FUNCTION_INFO_V1(postgres_fdw_validator);
Datum
postgres_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index b3aa6d1..236b6c3 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -19,7 +19,7 @@
#include "access/brin_page.h"
#include "access/brin_pageops.h"
#include "access/brin_xlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/relscan.h"
#include "access/xloginsert.h"
#include "catalog/index.h"
@@ -73,6 +73,7 @@ static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a,
BrinTuple *b);
static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy);
+static void *bringetreloptcatalog(void);
/*
* BRIN handler function: return IndexAmRoutine with access method parameters
@@ -106,7 +107,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = brinvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = brincostestimate;
- amroutine->amoptions = brinoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = brinvalidate;
amroutine->ambeginscan = brinbeginscan;
@@ -116,6 +116,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = brinendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = bringetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -800,37 +801,6 @@ brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
}
/*
- * reloptions processor for BRIN indexes
- */
-bytea *
-brinoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- BrinOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"pages_per_range", RELOPT_TYPE_INT, offsetof(BrinOptions, pagesPerRange)},
- {"autosummarize", RELOPT_TYPE_BOOL, offsetof(BrinOptions, autosummarize)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_BRIN,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(BrinOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(BrinOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
-/*
* SQL-callable function to scan through an index and summarize all ranges
* that are not currently summarized.
*/
@@ -1428,3 +1398,33 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy)
if (vacuum_fsm)
FreeSpaceMapVacuum(idxrel);
}
+
+static options_catalog *brin_relopt_catalog = NULL;
+
+static void *
+bringetreloptcatalog(void)
+{
+ if (!brin_relopt_catalog)
+ {
+ brin_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(BrinOptions), 2);
+
+ optionsCatalogAddItemInt(brin_relopt_catalog, "pages_per_range",
+ "Number of pages that each page range covers in a BRIN index",
+ NoLock, /* since ALTER is not allowed
+ * no lock needed */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER,
+ offsetof(BrinOptions, pagesPerRange),
+ BRIN_DEFAULT_PAGES_PER_RANGE,
+ BRIN_MIN_PAGES_PER_RANGE,
+ BRIN_MAX_PAGES_PER_RANGE);
+
+ optionsCatalogAddItemBool(brin_relopt_catalog, "autosummarize",
+ "Enables automatic summarization on this BRIN index",
+ AccessExclusiveLock,
+ 0,
+ offsetof(BrinOptions, autosummarize),
+ false);
+ }
+ return brin_relopt_catalog;
+}
diff --git a/src/backend/access/common/Makefile b/src/backend/access/common/Makefile
index f130b6e..ef305e6 100644
--- a/src/backend/access/common/Makefile
+++ b/src/backend/access/common/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/common
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = bufmask.o heaptuple.o indextuple.o printsimple.o printtup.o \
+OBJS = bufmask.o heaptuple.o indextuple.o options.o printsimple.o printtup.o \
reloptions.o scankey.o session.o tupconvert.o tupdesc.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 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.c
index ec10762..19816d5 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1,7 +1,10 @@
/*-------------------------------------------------------------------------
*
* reloptions.c
- * Core support for relation options (pg_class.reloptions)
+ * Support for options relotions (pg_class.reloptions).
+ * Reloptions for non-Access Metod relations are defined here.
+ * There is also extractRelOptions function to extract reloptions
+ * from pg_class table for all kind of relations.
*
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -17,928 +20,16 @@
#include <float.h>
-#include "access/gist_private.h"
-#include "access/hash.h"
#include "access/htup_details.h"
-#include "access/nbtree.h"
+#include "access/options.h"
#include "access/reloptions.h"
-#include "access/spgist.h"
-#include "catalog/pg_type.h"
-#include "commands/defrem.h"
#include "commands/tablespace.h"
-#include "commands/view.h"
-#include "nodes/makefuncs.h"
-#include "postmaster/postmaster.h"
-#include "utils/array.h"
#include "utils/attoptcache.h"
-#include "utils/builtins.h"
-#include "utils/guc.h"
-#include "utils/memutils.h"
#include "utils/rel.h"
+#include "storage/bufmgr.h"
-/*
- * Contents of pg_class.reloptions
- *
- * To add an option:
- *
- * (i) decide on a type (integer, real, bool, string), name, default value,
- * upper and lower bounds (if applicable); for strings, consider a validation
- * routine.
- * (ii) add a record below (or use add_<type>_reloption).
- * (iii) add it to the appropriate options struct (perhaps StdRdOptions)
- * (iv) add it to the appropriate handling routine (perhaps
- * default_reloptions)
- * (v) make sure the lock level is set correctly for that operation
- * (vi) don't forget to document the option
- *
- * Note that we don't handle "oids" in relOpts because it is handled by
- * interpretOidsOption().
- *
- * The default choice for any new option should be AccessExclusiveLock.
- * In some cases the lock level can be reduced from there, but the lock
- * level chosen should always conflict with itself to ensure that multiple
- * changes aren't lost when we attempt concurrent changes.
- * The choice of lock level depends completely upon how that parameter
- * is used within the server, not upon how and when you'd like to change it.
- * Safety first. Existing choices are documented here, and elsewhere in
- * backend code where the parameters are used.
- *
- * In general, anything that affects the results obtained from a SELECT must be
- * protected by AccessExclusiveLock.
- *
- * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
- * since they are only used by the AV procs and don't change anything
- * currently executing.
- *
- * Fillfactor can be set because it applies only to subsequent changes made to
- * data blocks, as documented in heapio.c
- *
- * n_distinct options can be set at ShareUpdateExclusiveLock because they
- * are only used during ANALYZE, which uses a ShareUpdateExclusiveLock,
- * so the ANALYZE will not be affected by in-flight changes. Changing those
- * values has no affect until the next ANALYZE, so no need for stronger lock.
- *
- * Planner-related parameters can be set with ShareUpdateExclusiveLock because
- * they only affect planning and not the correctness of the execution. Plans
- * cannot be changed in mid-flight, so changes here could not easily result in
- * new improved plans in any case. So we allow existing queries to continue
- * and existing plans to survive, a small price to pay for allowing better
- * plans to be introduced concurrently without interfering with users.
- *
- * Setting parallel_workers is safe, since it acts the same as
- * max_parallel_workers_per_gather which is a USERSET parameter that doesn't
- * affect existing plans or queries.
- */
-
-static relopt_bool boolRelOpts[] =
-{
- {
- {
- "autosummarize",
- "Enables automatic summarization on this BRIN index",
- RELOPT_KIND_BRIN,
- AccessExclusiveLock
- },
- false
- },
- {
- {
- "autovacuum_enabled",
- "Enables autovacuum in this relation",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- true
- },
- {
- {
- "user_catalog_table",
- "Declare a table as an additional catalog table, e.g. for the purpose of logical replication",
- RELOPT_KIND_HEAP,
- AccessExclusiveLock
- },
- false
- },
- {
- {
- "fastupdate",
- "Enables \"fast update\" feature for this GIN index",
- RELOPT_KIND_GIN,
- AccessExclusiveLock
- },
- true
- },
- {
- {
- "security_barrier",
- "View acts as a row security barrier",
- RELOPT_KIND_VIEW,
- AccessExclusiveLock
- },
- false
- },
- /* list terminator */
- {{NULL}}
-};
-
-static relopt_int intRelOpts[] =
-{
- {
- {
- "fillfactor",
- "Packs table pages only to this percentage",
- RELOPT_KIND_HEAP,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100
- },
- {
- {
- "fillfactor",
- "Packs btree index pages only to this percentage",
- RELOPT_KIND_BTREE,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
- },
- {
- {
- "fillfactor",
- "Packs hash index pages only to this percentage",
- RELOPT_KIND_HASH,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100
- },
- {
- {
- "fillfactor",
- "Packs gist index pages only to this percentage",
- RELOPT_KIND_GIST,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
- },
- {
- {
- "fillfactor",
- "Packs spgist index pages only to this percentage",
- RELOPT_KIND_SPGIST,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100
- },
- {
- {
- "autovacuum_vacuum_threshold",
- "Minimum number of tuple updates or deletes prior to vacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0, INT_MAX
- },
- {
- {
- "autovacuum_analyze_threshold",
- "Minimum number of tuple inserts, updates or deletes prior to analyze",
- RELOPT_KIND_HEAP,
- ShareUpdateExclusiveLock
- },
- -1, 0, INT_MAX
- },
- {
- {
- "autovacuum_vacuum_cost_delay",
- "Vacuum cost delay in milliseconds, for autovacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0, 100
- },
- {
- {
- "autovacuum_vacuum_cost_limit",
- "Vacuum cost amount available before napping, for autovacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 1, 10000
- },
- {
- {
- "autovacuum_freeze_min_age",
- "Minimum age at which VACUUM should freeze a table row, for autovacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0, 1000000000
- },
- {
- {
- "autovacuum_multixact_freeze_min_age",
- "Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0, 1000000000
- },
- {
- {
- "autovacuum_freeze_max_age",
- "Age at which to autovacuum a table to prevent transaction ID wraparound",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 100000, 2000000000
- },
- {
- {
- "autovacuum_multixact_freeze_max_age",
- "Multixact age at which to autovacuum a table to prevent multixact wraparound",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 10000, 2000000000
- },
- {
- {
- "autovacuum_freeze_table_age",
- "Age at which VACUUM should perform a full table sweep to freeze row versions",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- }, -1, 0, 2000000000
- },
- {
- {
- "autovacuum_multixact_freeze_table_age",
- "Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- }, -1, 0, 2000000000
- },
- {
- {
- "log_autovacuum_min_duration",
- "Sets the minimum execution time above which autovacuum actions will be logged",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, -1, INT_MAX
- },
- {
- {
- "pages_per_range",
- "Number of pages that each page range covers in a BRIN index",
- RELOPT_KIND_BRIN,
- AccessExclusiveLock
- }, 128, 1, 131072
- },
- {
- {
- "gin_pending_list_limit",
- "Maximum size of the pending list for this GIN index, in kilobytes.",
- RELOPT_KIND_GIN,
- AccessExclusiveLock
- },
- -1, 64, MAX_KILOBYTES
- },
- {
- {
- "effective_io_concurrency",
- "Number of simultaneous requests that can be handled efficiently by the disk subsystem.",
- RELOPT_KIND_TABLESPACE,
- ShareUpdateExclusiveLock
- },
-#ifdef USE_PREFETCH
- -1, 0, MAX_IO_CONCURRENCY
-#else
- 0, 0, 0
-#endif
- },
- {
- {
- "parallel_workers",
- "Number of parallel processes that can be used per executor node for this relation.",
- RELOPT_KIND_HEAP,
- ShareUpdateExclusiveLock
- },
- -1, 0, 1024
- },
-
- /* list terminator */
- {{NULL}}
-};
-
-static relopt_real realRelOpts[] =
-{
- {
- {
- "autovacuum_vacuum_scale_factor",
- "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, 100.0
- },
- {
- {
- "autovacuum_analyze_scale_factor",
- "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
- RELOPT_KIND_HEAP,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, 100.0
- },
- {
- {
- "seq_page_cost",
- "Sets the planner's estimate of the cost of a sequentially fetched disk page.",
- RELOPT_KIND_TABLESPACE,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, DBL_MAX
- },
- {
- {
- "random_page_cost",
- "Sets the planner's estimate of the cost of a nonsequentially fetched disk page.",
- RELOPT_KIND_TABLESPACE,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, DBL_MAX
- },
- {
- {
- "n_distinct",
- "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
- RELOPT_KIND_ATTRIBUTE,
- ShareUpdateExclusiveLock
- },
- 0, -1.0, DBL_MAX
- },
- {
- {
- "n_distinct_inherited",
- "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
- RELOPT_KIND_ATTRIBUTE,
- ShareUpdateExclusiveLock
- },
- 0, -1.0, DBL_MAX
- },
- /* list terminator */
- {{NULL}}
-};
-
-static relopt_string stringRelOpts[] =
-{
- {
- {
- "buffering",
- "Enables buffering build for this GiST index",
- RELOPT_KIND_GIST,
- AccessExclusiveLock
- },
- 4,
- false,
- gistValidateBufferingOption,
- "auto"
- },
- {
- {
- "check_option",
- "View has WITH CHECK OPTION defined (local or cascaded).",
- RELOPT_KIND_VIEW,
- AccessExclusiveLock
- },
- 0,
- true,
- validateWithCheckOption,
- NULL
- },
- /* list terminator */
- {{NULL}}
-};
-
-static relopt_gen **relOpts = NULL;
-static bits32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;
-
-static int num_custom_options = 0;
-static relopt_gen **custom_options = NULL;
-static bool need_initialization = true;
-
-static void initialize_reloptions(void);
-static void parse_one_reloption(relopt_value *option, char *text_str,
- int text_len, bool validate);
-
-/*
- * initialize_reloptions
- * initialization routine, must be called before parsing
- *
- * Initialize the relOpts array and fill each variable's type and name length.
- */
-static void
-initialize_reloptions(void)
-{
- int i;
- int j;
-
- j = 0;
- for (i = 0; boolRelOpts[i].gen.name; i++)
- {
- Assert(DoLockModesConflict(boolRelOpts[i].gen.lockmode,
- boolRelOpts[i].gen.lockmode));
- j++;
- }
- for (i = 0; intRelOpts[i].gen.name; i++)
- {
- Assert(DoLockModesConflict(intRelOpts[i].gen.lockmode,
- intRelOpts[i].gen.lockmode));
- j++;
- }
- for (i = 0; realRelOpts[i].gen.name; i++)
- {
- Assert(DoLockModesConflict(realRelOpts[i].gen.lockmode,
- realRelOpts[i].gen.lockmode));
- j++;
- }
- for (i = 0; stringRelOpts[i].gen.name; i++)
- {
- Assert(DoLockModesConflict(stringRelOpts[i].gen.lockmode,
- stringRelOpts[i].gen.lockmode));
- j++;
- }
- j += num_custom_options;
-
- if (relOpts)
- pfree(relOpts);
- relOpts = MemoryContextAlloc(TopMemoryContext,
- (j + 1) * sizeof(relopt_gen *));
-
- j = 0;
- for (i = 0; boolRelOpts[i].gen.name; i++)
- {
- relOpts[j] = &boolRelOpts[i].gen;
- relOpts[j]->type = RELOPT_TYPE_BOOL;
- relOpts[j]->namelen = strlen(relOpts[j]->name);
- j++;
- }
-
- for (i = 0; intRelOpts[i].gen.name; i++)
- {
- relOpts[j] = &intRelOpts[i].gen;
- relOpts[j]->type = RELOPT_TYPE_INT;
- relOpts[j]->namelen = strlen(relOpts[j]->name);
- j++;
- }
-
- for (i = 0; realRelOpts[i].gen.name; i++)
- {
- relOpts[j] = &realRelOpts[i].gen;
- relOpts[j]->type = RELOPT_TYPE_REAL;
- relOpts[j]->namelen = strlen(relOpts[j]->name);
- j++;
- }
-
- for (i = 0; stringRelOpts[i].gen.name; i++)
- {
- relOpts[j] = &stringRelOpts[i].gen;
- relOpts[j]->type = RELOPT_TYPE_STRING;
- relOpts[j]->namelen = strlen(relOpts[j]->name);
- j++;
- }
-
- for (i = 0; i < num_custom_options; i++)
- {
- relOpts[j] = custom_options[i];
- j++;
- }
-
- /* add a list terminator */
- relOpts[j] = NULL;
-
- /* flag the work is complete */
- need_initialization = false;
-}
-
-/*
- * add_reloption_kind
- * Create a new relopt_kind value, to be used in custom reloptions by
- * user-defined AMs.
- */
-relopt_kind
-add_reloption_kind(void)
-{
- /* don't hand out the last bit so that the enum's behavior is portable */
- if (last_assigned_kind >= RELOPT_KIND_MAX)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("user-defined relation parameter types limit exceeded")));
- last_assigned_kind <<= 1;
- return (relopt_kind) last_assigned_kind;
-}
-
-/*
- * add_reloption
- * Add an already-created custom reloption to the list, and recompute the
- * main parser table.
- */
-static void
-add_reloption(relopt_gen *newoption)
-{
- static int max_custom_options = 0;
-
- if (num_custom_options >= max_custom_options)
- {
- MemoryContext oldcxt;
-
- oldcxt = MemoryContextSwitchTo(TopMemoryContext);
-
- if (max_custom_options == 0)
- {
- max_custom_options = 8;
- custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
- }
- else
- {
- max_custom_options *= 2;
- custom_options = repalloc(custom_options,
- max_custom_options * sizeof(relopt_gen *));
- }
- MemoryContextSwitchTo(oldcxt);
- }
- custom_options[num_custom_options++] = newoption;
-
- need_initialization = true;
-}
-
-/*
- * allocate_reloption
- * Allocate a new reloption and initialize the type-agnostic fields
- * (for types other than string)
- */
-static relopt_gen *
-allocate_reloption(bits32 kinds, int type, char *name, char *desc)
-{
- MemoryContext oldcxt;
- size_t size;
- relopt_gen *newoption;
-
- oldcxt = MemoryContextSwitchTo(TopMemoryContext);
-
- switch (type)
- {
- case RELOPT_TYPE_BOOL:
- size = sizeof(relopt_bool);
- break;
- case RELOPT_TYPE_INT:
- size = sizeof(relopt_int);
- break;
- case RELOPT_TYPE_REAL:
- size = sizeof(relopt_real);
- break;
- case RELOPT_TYPE_STRING:
- size = sizeof(relopt_string);
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", type);
- return NULL; /* keep compiler quiet */
- }
-
- newoption = palloc(size);
-
- newoption->name = pstrdup(name);
- if (desc)
- newoption->desc = pstrdup(desc);
- else
- newoption->desc = NULL;
- newoption->kinds = kinds;
- newoption->namelen = strlen(name);
- newoption->type = type;
-
- MemoryContextSwitchTo(oldcxt);
-
- return newoption;
-}
-
-/*
- * add_bool_reloption
- * Add a new boolean reloption
- */
-void
-add_bool_reloption(bits32 kinds, char *name, char *desc, bool default_val)
-{
- relopt_bool *newoption;
-
- newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
- name, desc);
- newoption->default_val = default_val;
-
- add_reloption((relopt_gen *) newoption);
-}
-
-/*
- * add_int_reloption
- * Add a new integer reloption
- */
-void
-add_int_reloption(bits32 kinds, char *name, char *desc, int default_val,
- int min_val, int max_val)
-{
- relopt_int *newoption;
-
- newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
- name, desc);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- add_reloption((relopt_gen *) newoption);
-}
-
-/*
- * add_real_reloption
- * Add a new float reloption
- */
-void
-add_real_reloption(bits32 kinds, char *name, char *desc, double default_val,
- double min_val, double max_val)
-{
- relopt_real *newoption;
-
- newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
- name, desc);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- add_reloption((relopt_gen *) newoption);
-}
-
-/*
- * add_string_reloption
- * Add a new string reloption
- *
- * "validator" is an optional function pointer that can be used to test the
- * validity of the values. It must elog(ERROR) when the argument string is
- * not acceptable for the variable. Note that the default value must pass
- * the validation.
- */
-void
-add_string_reloption(bits32 kinds, char *name, char *desc, char *default_val,
- validate_string_relopt validator)
-{
- relopt_string *newoption;
-
- /* make sure the validator/default combination is sane */
- if (validator)
- (validator) (default_val);
-
- newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
- name, desc);
- newoption->validate_cb = validator;
- if (default_val)
- {
- newoption->default_val = MemoryContextStrdup(TopMemoryContext,
- default_val);
- newoption->default_len = strlen(default_val);
- newoption->default_isnull = false;
- }
- else
- {
- newoption->default_val = "";
- newoption->default_len = 0;
- newoption->default_isnull = true;
- }
-
- add_reloption((relopt_gen *) newoption);
-}
-
-/*
- * Transform a relation options list (list of DefElem) into the text array
- * format that is kept in pg_class.reloptions, including only those options
- * that are in the passed namespace. The output values do not include the
- * namespace.
- *
- * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
- * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
- * reloptions value (possibly NULL), and we replace or remove entries
- * as needed.
- *
- * If ignoreOids is true, then we should ignore any occurrence of "oids"
- * in the list (it will be or has been handled by interpretOidsOption()).
- *
- * Note that this is not responsible for determining whether the options
- * are valid, but it does check that namespaces for all the options given are
- * listed in validnsps. The NULL namespace is always valid and need not be
- * explicitly listed. Passing a NULL pointer means that only the NULL
- * namespace is valid.
- *
- * Both oldOptions and the result are text arrays (or NULL for "default"),
- * but we declare them as Datums to avoid including array.h in reloptions.h.
- */
-Datum
-transformRelOptions(Datum oldOptions, List *defList, char *namspace,
- char *validnsps[], bool ignoreOids, bool isReset)
-{
- Datum result;
- ArrayBuildState *astate;
- ListCell *cell;
-
- /* no change if empty list */
- if (defList == NIL)
- return oldOptions;
-
- /* We build new array using accumArrayResult */
- astate = NULL;
-
- /* Copy any oldOptions that aren't to be replaced */
- if (PointerIsValid(DatumGetPointer(oldOptions)))
- {
- ArrayType *array = DatumGetArrayTypeP(oldOptions);
- Datum *oldoptions;
- int noldoptions;
- int i;
-
- deconstruct_array(array, TEXTOID, -1, false, 'i',
- &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- pg_strncasecmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (pg_strcasecmp(def->defnamespace,
- validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- if (ignoreOids && pg_strcasecmp(def->defname, "oids") == 0)
- continue;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
-/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
- */
-List *
-untransformRelOptions(Datum options)
-{
- List *result = NIL;
- ArrayType *array;
- Datum *optiondatums;
- int noptions;
- int i;
-
- /* Nothing to do if no options */
- if (!PointerIsValid(DatumGetPointer(options)))
- return result;
-
- array = DatumGetArrayTypeP(options);
-
- deconstruct_array(array, TEXTOID, -1, false, 'i',
- &optiondatums, NULL, &noptions);
-
- for (i = 0; i < noptions; i++)
- {
- char *s;
- char *p;
- Node *val = NULL;
-
- s = TextDatumGetCString(optiondatums[i]);
- p = strchr(s, '=');
- if (p)
- {
- *p++ = '\0';
- val = (Node *) makeString(pstrdup(p));
- }
- result = lappend(result, makeDefElem(pstrdup(s), val, -1));
- }
-
- return result;
-}
+void add_autovacuum_options(options_catalog * catalog, int base_offset,
+ bool for_toast);
/*
* Extract and parse reloptions from a pg_class tuple.
@@ -954,11 +45,12 @@ untransformRelOptions(Datum options)
*/
bytea *
extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions)
+ amrelopt_catalog_function catalog_fun)
{
- bytea *options;
+ bytea *options = NULL;
bool isnull;
Datum datum;
+ options_catalog *catalog = NULL;
Form_pg_class classForm;
datum = fastgetattr(tuple,
@@ -974,548 +66,321 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
switch (classForm->relkind)
{
case RELKIND_RELATION:
- case RELKIND_TOASTVALUE:
case RELKIND_MATVIEW:
- case RELKIND_PARTITIONED_TABLE:
- options = heap_reloptions(classForm->relkind, datum, false);
+ catalog = get_heap_relopt_catalog();
+ break;
+ case RELKIND_TOASTVALUE:
+ catalog = get_toast_relopt_catalog();
break;
case RELKIND_VIEW:
- options = view_reloptions(datum, false);
+ catalog = get_view_relopt_catalog();
break;
case RELKIND_INDEX:
- options = index_reloptions(amoptions, datum, false);
+ if (catalog_fun)
+ catalog = catalog_fun();
break;
case RELKIND_FOREIGN_TABLE:
options = NULL;
break;
+ case RELKIND_PARTITIONED_TABLE:
+ catalog = NULL; /* No options for parted table for now */
+ break;
default:
Assert(false); /* can't get here */
options = NULL; /* keep compiler quiet */
break;
}
+ if (catalog)
+ options = optionsTextArrayToBytea(catalog, datum);
return options;
}
-/*
- * Interpret reloptions that are given in text-array format.
- *
- * options is a reloption text array as constructed by transformRelOptions.
- * kind specifies the family of options to be processed.
- *
- * The return value is a relopt_value * array on which the options actually
- * set in the options array are marked with isset=true. The length of this
- * array is returned in *numrelopts. Options not set are also present in the
- * array; this is so that the caller can easily locate the default values.
- *
- * If there are no options of the given kind, numrelopts is set to 0 and NULL
- * is returned (unless options are illegally supplied despite none being
- * defined, in which case an error occurs).
- *
- * Note: values of type int, bool and real are allocated as part of the
- * returned array. Values of type string are allocated separately and must
- * be freed by the caller.
- */
-relopt_value *
-parseRelOptions(Datum options, bool validate, relopt_kind kind,
- int *numrelopts)
-{
- relopt_value *reloptions = NULL;
- int numoptions = 0;
- int i;
- int j;
-
- if (need_initialization)
- initialize_reloptions();
-
- /* Build a list of expected options, based on kind */
-
- for (i = 0; relOpts[i]; i++)
- if (relOpts[i]->kinds & kind)
- numoptions++;
-
- if (numoptions > 0)
- {
- reloptions = palloc(numoptions * sizeof(relopt_value));
-
- for (i = 0, j = 0; relOpts[i]; i++)
- {
- if (relOpts[i]->kinds & kind)
- {
- reloptions[j].gen = relOpts[i];
- reloptions[j].isset = false;
- j++;
- }
- }
- }
-
- /* Done if no options */
- if (PointerIsValid(DatumGetPointer(options)))
- {
- ArrayType *array = DatumGetArrayTypeP(options);
- Datum *optiondatums;
- int noptions;
-
- deconstruct_array(array, TEXTOID, -1, false, 'i',
- &optiondatums, NULL, &noptions);
-
- for (i = 0; i < noptions; i++)
- {
- char *text_str = VARDATA(optiondatums[i]);
- int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
- int j;
-
- /* Search for a match in reloptions */
- for (j = 0; j < numoptions; j++)
- {
- int kw_len = reloptions[j].gen->namelen;
-
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- pg_strncasecmp(text_str, reloptions[j].gen->name,
- kw_len) == 0)
- {
- parse_one_reloption(&reloptions[j], text_str, text_len,
- validate);
- break;
- }
- }
-
- if (j >= numoptions && validate)
- {
- char *s;
- char *p;
-
- s = TextDatumGetCString(optiondatums[i]);
- p = strchr(s, '=');
- if (p)
- *p = '\0';
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter \"%s\"", s)));
- }
- }
-
- /* It's worth avoiding memory leaks in this function */
- pfree(optiondatums);
- if (((void *) array) != DatumGetPointer(options))
- pfree(array);
- }
-
- *numrelopts = numoptions;
- return reloptions;
-}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * Here goes functions that build catalogs for non-index relations. Catalogs
+ * for index relations are available from Access Methods, all other
+ * relation catalogs are here
*/
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
-
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
-
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
- }
-
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
-}
/*
- * Given the result from parseRelOptions, allocate a struct that's of the
- * specified base size plus any extra space that's needed for string variables.
+ * Both heap and toast relation have almost the same autovacuum set of options.
+ * These autovacuum options are stored in the same structure (AutoVacOpts)
+ * that is a part of both HeapOptions and ToastOptions.
*
- * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
- * equivalent).
- */
-void *
-allocateReloptStruct(Size base, relopt_value *options, int numoptions)
-{
- Size size = base;
- int i;
-
- for (i = 0; i < numoptions; i++)
- if (options[i].gen->type == RELOPT_TYPE_STRING)
- size += GET_STRING_RELOPTION_LEN(options[i]) + 1;
-
- return palloc0(size);
-}
-
-/*
- * Given the result of parseRelOptions and a parsing table, fill in the
- * struct (previously allocated with allocateReloptStruct) with the parsed
- * values.
+ * Function add_autovacuum_options adds to the "catalog" catalog autovacuum
+ * options definition. In binary mode this options are saved into AutoVacOpts
+ * structure, which is located in result bytea chunk with base_offset offset.
*
- * rdopts is the pointer to the allocated struct to be filled.
- * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
- * options, of length numoptions, is parseRelOptions' output.
- * elems, of length numelems, is the table describing the allowed options.
- * When validate is true, it is expected that all options appear in elems.
+ * Heap has two options (autovacuum_analyze_threshold and
+ * autovacuum_analyze_scale_factor) that is not used for toast. So for toast
+ * case this options are added to catalog with OPTION_DEFINITION_FLAG_REJECT
+ * flag set, so postgres will reject this toast options from the CREATE or
+ * ALTER command, but still will save default value in binary representation
*/
+
void
-fillRelOptions(void *rdopts, Size basesize,
- relopt_value *options, int numoptions,
- bool validate,
- const relopt_parse_elt *elems, int numelems)
+add_autovacuum_options(options_catalog * catalog, int base_offset,
+ bool for_toast)
{
- int i;
- int offset = basesize;
-
- for (i = 0; i < numoptions; i++)
- {
- int j;
- bool found = false;
-
- for (j = 0; j < numelems; j++)
- {
- if (pg_strcasecmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+ optionsCatalogAddItemBool(catalog, "autovacuum_enabled",
+ "Enables autovacuum in this relation",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, enabled), true);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_threshold",
+ "Minimum number of tuple updates or deletes prior to vacuum",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, vacuum_threshold),
+ -1, 0, INT_MAX);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_analyze_threshold",
+ "Minimum number of tuple updates or deletes prior to vacuum",
+ ShareUpdateExclusiveLock,
+ for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0,
+ base_offset + offsetof(AutoVacOpts, analyze_threshold),
+ -1, 0, INT_MAX);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_cost_delay",
+ "Vacuum cost delay in milliseconds, for autovacuum",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, vacuum_cost_delay),
+ -1, 0, 100);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_cost_limit",
+ "Vacuum cost amount available before napping, for autovacuum",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, vacuum_cost_limit),
+ -1, 0, 10000);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_freeze_min_age",
+ "Minimum age at which VACUUM should freeze a table row, for autovacuum",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, freeze_min_age),
+ -1, 0, 1000000000);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_freeze_max_age",
+ "Age at which to autovacuum a table to prevent transaction ID wraparound",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, freeze_max_age),
+ -1, 100000, 2000000000);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_freeze_table_age",
+ "Age at which VACUUM should perform a full table sweep to freeze row versions",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, freeze_table_age),
+ -1, 0, 2000000000);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_min_age",
+ "Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, multixact_freeze_min_age),
+ -1, 0, 1000000000);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_max_age",
+ "Multixact age at which to autovacuum a table to prevent multixact wraparound",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, multixact_freeze_max_age),
+ -1, 10000, 2000000000);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_table_age",
+ "Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, multixact_freeze_table_age),
+ -1, 0, 2000000000);
+
+ optionsCatalogAddItemInt(catalog, "log_autovacuum_min_duration",
+ "Sets the minimum execution time above which autovacuum actions will be logged",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, log_min_duration),
+ -1, -1, INT_MAX);
+
+ optionsCatalogAddItemReal(catalog, "autovacuum_vacuum_scale_factor",
+ "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, vacuum_scale_factor),
+ -1, 0.0, 100.0);
+
+ optionsCatalogAddItemReal(catalog, "autovacuum_analyze_scale_factor",
+ "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
+ ShareUpdateExclusiveLock,
+ for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0,
+ base_offset + offsetof(AutoVacOpts, analyze_scale_factor),
+ -1, 0.0, 100.0);
}
-
/*
- * Option parser for anything that uses StdRdOptions.
+ * get_heap_relopt_catalog
+ * Returns an options catalog for heap relation.
*/
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
-{
- relopt_value *options;
- StdRdOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
- {"autovacuum_enabled", RELOPT_TYPE_BOOL,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
- {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)},
- {"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
- {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)},
- {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
- {"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
- {"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
- {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
- {"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age)},
- {"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)},
- {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
- {"log_autovacuum_min_duration", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)},
- {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
- {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
- offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)},
- {"user_catalog_table", RELOPT_TYPE_BOOL,
- offsetof(StdRdOptions, user_catalog_table)},
- {"parallel_workers", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, parallel_workers)}
- };
+static options_catalog *heap_relopt_catalog = NULL;
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(StdRdOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
+options_catalog *
+get_heap_relopt_catalog(void)
+{
+ if (!heap_relopt_catalog)
+ {
+ heap_relopt_catalog = allocateOptionsCatalog(NULL, sizeof(HeapOptions),
+ 14 + 4); /* 14 - for autovacuum
+ * options, 4 - core
+ * heap options */
+
+ optionsCatalogAddItemInt(heap_relopt_catalog, "fillfactor",
+ "Packs table pages only to this percentag",
+ ShareUpdateExclusiveLock, /* since it applies only
+ * to later inserts */
+ 0, offsetof(HeapOptions, fillfactor),
+ HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100);
+
+ add_autovacuum_options(heap_relopt_catalog,
+ offsetof(HeapOptions, autovacuum), false);
+
+ optionsCatalogAddItemBool(heap_relopt_catalog, "user_catalog_table",
+ "Declare a table as an additional catalog table, e.g. for the purpose of logical replication",
+ AccessExclusiveLock,
+ 0, offsetof(HeapOptions, user_catalog_table),
+ false);
+
+ optionsCatalogAddItemInt(heap_relopt_catalog, "parallel_workers",
+ "Number of parallel processes that can be used per executor node for this relation",
+ ShareUpdateExclusiveLock,
+ 0, offsetof(HeapOptions, parallel_workers),
+ -1, 0, 1024);
+
+ /*
+ * A WITH OID / WITHOUT OIDS expressions are converted by syntax
+ * parser into "oids" relation option, but this option is processed by
+ * interpretOidsOption(), that does not use core options code. That is
+ * why here we should just ignore this option as if it does not exist.
+ * Do it with OPTION_DEFINITION_FLAG_IGNORE flag
+ */
+ optionsCatalogAddItemBool(heap_relopt_catalog, "oids",
+ "Option used for WITH OIDS expression. Processed elsewhere",
+ NoLock,
+ OPTION_DEFINITION_FLAG_IGNORE, -1,
+ false);
+ }
+ return heap_relopt_catalog;
}
/*
- * Option parser for views
+ * get_toast_relopt_catalog
+ * Returns an options catalog for toast relation.
*/
-bytea *
-view_reloptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- ViewOptions *vopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"security_barrier", RELOPT_TYPE_BOOL,
- offsetof(ViewOptions, security_barrier)},
- {"check_option", RELOPT_TYPE_STRING,
- offsetof(ViewOptions, check_option_offset)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_VIEW, &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
- vopts = allocateReloptStruct(sizeof(ViewOptions), options, numoptions);
+static options_catalog *toast_relopt_catalog = NULL;
- fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) vopts;
-}
-
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_catalog *
+get_toast_relopt_catalog(void)
{
- StdRdOptions *rdopts;
-
- switch (relkind)
+ if (!toast_relopt_catalog)
{
- case RELKIND_TOASTVALUE:
- rdopts = (StdRdOptions *)
- default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
- if (rdopts != NULL)
- {
- /* adjust default-only parameters for TOAST relations */
- rdopts->fillfactor = 100;
- rdopts->autovacuum.analyze_threshold = -1;
- rdopts->autovacuum.analyze_scale_factor = -1;
- }
- return (bytea *) rdopts;
- case RELKIND_RELATION:
- case RELKIND_MATVIEW:
- return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
- case RELKIND_PARTITIONED_TABLE:
- return default_reloptions(reloptions, validate,
- RELOPT_KIND_PARTITIONED);
- default:
- /* other relkinds are not supported */
- return NULL;
+ toast_relopt_catalog = allocateOptionsCatalog("toast", sizeof(ToastOptions),
+ 14); /* 14 - for autovacuum
+ * options */
+ add_autovacuum_options(toast_relopt_catalog,
+ offsetof(ToastOptions, autovacuum), true);
}
+ return toast_relopt_catalog;
}
-
/*
- * Parse options for indexes.
- *
- * amoptions index AM's option parser function
- * reloptions options as text[] datum
- * validate error flag
+ * get_view_relopt_catalog
+ * Returns an options catalog for view relation.
*/
-bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
+static options_catalog *view_relopt_catalog = NULL;
+
+options_catalog *
+get_view_relopt_catalog(void)
{
- Assert(amoptions != NULL);
+ static const char *enum_names[] = VIEW_OPTION_CHECK_OPTION_VALUE_NAMES;
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ if (!view_relopt_catalog)
+ {
+ view_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(ViewOptions), 2);
- return amoptions(reloptions, validate);
+ optionsCatalogAddItemBool(view_relopt_catalog, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ 0, offsetof(ViewOptions, security_barrier), false);
+
+ optionsCatalogAddItemEnum(view_relopt_catalog, "check_option",
+ "View has WITH CHECK OPTION defined (local or cascaded)",
+ AccessExclusiveLock, 0,
+ offsetof(ViewOptions, check_option),
+ enum_names,
+ VIEW_OPTION_CHECK_OPTION_NOT_SET);
+ }
+ return view_relopt_catalog;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_catalog
+ * Returns an options catalog for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- AttributeOpts *aopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
- {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- aopts = allocateReloptStruct(sizeof(AttributeOpts), options, numoptions);
+static options_catalog *attribute_options_catalog = NULL;
- fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
- validate, tab, lengthof(tab));
+options_catalog *
+get_attribute_options_catalog(void)
+{
+ if (!attribute_options_catalog)
+ {
+ attribute_options_catalog = allocateOptionsCatalog(NULL,
+ sizeof(AttributeOpts), 2);
- pfree(options);
+ optionsCatalogAddItemReal(attribute_options_catalog, "n_distinct",
+ "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations)",
+ ShareUpdateExclusiveLock,
+ 0, offsetof(AttributeOpts, n_distinct), 0, -1.0, DBL_MAX);
- return (bytea *) aopts;
+ optionsCatalogAddItemReal(attribute_options_catalog,
+ "n_distinct_inherited",
+ "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations",
+ ShareUpdateExclusiveLock,
+ 0, offsetof(AttributeOpts, n_distinct_inherited), 0, -1.0, DBL_MAX);
+ }
+ return attribute_options_catalog;
}
/*
- * Option parser for tablespace reloptions
+ * get_tablespace_options_catalog
+ * Returns an options catalog for tablespaces
*/
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- TableSpaceOpts *tsopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
- {"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)},
- {"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, effective_io_concurrency)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);
-
- fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
+static options_catalog *tablespace_options_catalog = NULL;
- return (bytea *) tsopts;
+options_catalog *
+get_tablespace_options_catalog(void)
+{
+ if (!tablespace_options_catalog)
+ {
+ tablespace_options_catalog = allocateOptionsCatalog(NULL,
+ sizeof(TableSpaceOpts), 3);
+
+ optionsCatalogAddItemReal(tablespace_options_catalog,
+ "random_page_cost",
+ "Sets the planner's estimate of the cost of a nonsequentially fetched disk page",
+ ShareUpdateExclusiveLock,
+ 0, offsetof(TableSpaceOpts, random_page_cost), -1, 0.0, DBL_MAX);
+
+ optionsCatalogAddItemReal(tablespace_options_catalog, "seq_page_cost",
+ "Sets the planner's estimate of the cost of a sequentially fetched disk page",
+ ShareUpdateExclusiveLock,
+ 0, offsetof(TableSpaceOpts, seq_page_cost), -1, 0.0, DBL_MAX);
+
+ optionsCatalogAddItemInt(tablespace_options_catalog,
+ "effective_io_concurrency",
+ "Number of simultaneous requests that can be handled efficiently by the disk subsystem",
+ ShareUpdateExclusiveLock,
+ 0, offsetof(TableSpaceOpts, effective_io_concurrency),
+#ifdef USE_PREFETCH
+ -1, 0, MAX_IO_CONCURRENCY
+#else
+ 0, 0, 0
+#endif
+ );
+ }
+ return tablespace_options_catalog;
}
/*
@@ -1525,33 +390,56 @@ tablespace_reloptions(Datum reloptions, bool validate)
* for a longer explanation of how this works.
*/
LOCKMODE
-AlterTableGetRelOptionsLockLevel(List *defList)
+AlterTableGetRelOptionsLockLevel(Relation rel, List *defList)
{
LOCKMODE lockmode = NoLock;
ListCell *cell;
+ options_catalog *catalog = NULL;
if (defList == NIL)
return AccessExclusiveLock;
- if (need_initialization)
- initialize_reloptions();
+ switch (rel->rd_rel->relkind)
+ {
+ case RELKIND_TOASTVALUE:
+ catalog = get_toast_relopt_catalog();
+ break;
+ case RELKIND_RELATION:
+ case RELKIND_MATVIEW:
+ catalog = get_heap_relopt_catalog();
+ break;
+ case RELKIND_INDEX:
+ catalog = rel->rd_amroutine->amrelopt_catalog();
+ break;
+ case RELKIND_VIEW:
+ catalog = get_view_relopt_catalog();
+ break;
+ case RELKIND_PARTITIONED_TABLE:
+ catalog = NULL; /* No options for parted table for now */
+ break;
+ default:
+ Assert(false); /* can't get here */
+ break;
+ }
+
+ Assert(catalog); /* No catalog - no reloption change. Should
+ * not get here */
foreach(cell, defList)
{
DefElem *def = (DefElem *) lfirst(cell);
+
int i;
- for (i = 0; relOpts[i]; i++)
+ for (i = 0; i < catalog->num; i++)
{
- if (pg_strncasecmp(relOpts[i]->name,
- def->defname,
- relOpts[i]->namelen + 1) == 0)
- {
- if (lockmode < relOpts[i]->lockmode)
- lockmode = relOpts[i]->lockmode;
- }
+ option_definition_basic *gen = catalog->definitions[i];
+
+ if (pg_strcasecmp(gen->name,
+ def->defname) == 0)
+ if (lockmode < gen->lockmode)
+ lockmode = gen->lockmode;
}
}
-
return lockmode;
}
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 136ea27..16b96c6 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -16,7 +16,7 @@
#include "access/gin_private.h"
#include "access/ginxlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/xloginsert.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
@@ -26,7 +26,8 @@
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/typcache.h"
-
+#include "utils/memutils.h"
+#include "utils/guc.h"
/*
* GIN handler function: return IndexAmRoutine with access method parameters
@@ -60,7 +61,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = ginvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = ginvalidate;
amroutine->ambeginscan = ginbeginscan;
@@ -70,6 +70,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = ginendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = gingetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -595,35 +596,6 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
return entries;
}
-bytea *
-ginoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- GinOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)},
- {"gin_pending_list_limit", RELOPT_TYPE_INT, offsetof(GinOptions,
- pendingListCleanupSize)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
/*
* Fetch index's statistical data into *stats
*
@@ -700,3 +672,32 @@ ginUpdateStats(Relation index, const GinStatsData *stats)
END_CRIT_SECTION();
}
+
+
+static options_catalog *gin_relopt_catalog = NULL;
+
+void *
+gingetreloptcatalog(void)
+{
+ if (!gin_relopt_catalog)
+ {
+ gin_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(GinOptions), 2);
+
+ optionsCatalogAddItemBool(gin_relopt_catalog, "fastupdate",
+ "Enables \"fast update\" feature for this GIN index",
+ AccessExclusiveLock,
+ 0,
+ offsetof(GinOptions, useFastUpdate),
+ GIN_DEFAULT_USE_FASTUPDATE);
+
+ optionsCatalogAddItemInt(gin_relopt_catalog, "gin_pending_list_limit",
+ "Maximum size of the pending list for this GIN index, in kilobytes",
+ AccessExclusiveLock,
+ 0,
+ offsetof(GinOptions, pendingListCleanupSize),
+ -1, 64, MAX_KILOBYTES);
+
+ }
+ return gin_relopt_catalog;
+}
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index aec174c..c42a375 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -81,7 +81,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = gistvacuumcleanup;
amroutine->amcanreturn = gistcanreturn;
amroutine->amcostestimate = gistcostestimate;
- amroutine->amoptions = gistoptions;
amroutine->amproperty = gistproperty;
amroutine->amvalidate = gistvalidate;
amroutine->ambeginscan = gistbeginscan;
@@ -91,6 +90,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = gistendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = gistgetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index b4cb364..d80d604 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -126,11 +126,10 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
{
/* Get buffering mode from the options string */
GiSTOptions *options = (GiSTOptions *) index->rd_options;
- char *bufferingMode = (char *) options + options->bufferingModeOffset;
- if (strcmp(bufferingMode, "on") == 0)
+ if (options->buffering_mode == GIST_OPTION_BUFFERING_ON)
buildstate.bufferingMode = GIST_BUFFERING_STATS;
- else if (strcmp(bufferingMode, "off") == 0)
+ else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF)
buildstate.bufferingMode = GIST_BUFFERING_DISABLED;
else
buildstate.bufferingMode = GIST_BUFFERING_AUTO;
@@ -234,25 +233,6 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
}
/*
- * Validator for "buffering" reloption on GiST indexes. Allows "on", "off"
- * and "auto" values.
- */
-void
-gistValidateBufferingOption(char *value)
-{
- if (value == NULL ||
- (strcmp(value, "on") != 0 &&
- strcmp(value, "off") != 0 &&
- strcmp(value, "auto") != 0))
- {
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for \"buffering\" option"),
- errdetail("Valid values are \"on\", \"off\", and \"auto\".")));
- }
-}
-
-/*
* Attempt to switch to buffering mode.
*
* If there is not enough memory for buffering build, sets bufferingMode
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 26d89f7..ee322ba 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -18,14 +18,13 @@
#include "access/gist_private.h"
#include "access/htup_details.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "catalog/pg_opclass.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
-
/*
* Write itup vector to page, has no control of free space.
*/
@@ -832,34 +831,6 @@ gistNewBuffer(Relation r)
return buffer;
}
-bytea *
-gistoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- GiSTOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
- {"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOffset)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(GiSTOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
/*
* gistproperty() -- Check boolean properties of indexes.
*
@@ -999,3 +970,33 @@ gistGetFakeLSN(Relation rel)
return GetFakeLSNForUnloggedRel();
}
}
+
+static options_catalog *gist_relopt_catalog = NULL;
+
+void *
+gistgetreloptcatalog(void)
+{
+ static const char *enum_names[] = GIST_OPTION_BUFFERING_VALUE_NAMES;
+
+ if (!gist_relopt_catalog)
+ {
+ gist_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(GiSTOptions), 2);
+
+ optionsCatalogAddItemInt(gist_relopt_catalog, "fillfactor",
+ "Packs gist index pages only to this percentage",
+ NoLock, /* No ALTER, no lock */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER,
+ offsetof(GiSTOptions, fillfactor),
+ GIST_DEFAULT_FILLFACTOR,
+ GIST_MIN_FILLFACTOR, 100);
+ optionsCatalogAddItemEnum(gist_relopt_catalog, "buffering",
+ "Enables buffering build for this GiST index",
+ NoLock, /* No ALTER, no lock */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER,
+ offsetof(GiSTOptions, buffering_mode),
+ enum_names,
+ GIST_OPTION_BUFFERING_AUTO);
+ }
+ return gist_relopt_catalog;
+}
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 0fef60a..75c0ba9 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -79,7 +79,6 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = hashvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = hashcostestimate;
- amroutine->amoptions = hashoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = hashvalidate;
amroutine->ambeginscan = hashbeginscan;
@@ -89,6 +88,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = hashendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = hashgetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index f279dce..a841cfc 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -368,7 +368,7 @@ _hash_init(Relation rel, double num_tuples, ForkNumber forkNum)
data_width = sizeof(uint32);
item_width = MAXALIGN(sizeof(IndexTupleData)) + MAXALIGN(data_width) +
sizeof(ItemIdData); /* include the line pointer */
- ffactor = RelationGetTargetPageUsage(rel, HASH_DEFAULT_FILLFACTOR) / item_width;
+ ffactor = (BLCKSZ * HashGetFillFactor(rel) / 100) / item_width;
/* keep to a sane range */
if (ffactor < 10)
ffactor = 10;
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
index f2a1c5b..eb45362 100644
--- a/src/backend/access/hash/hashutil.c
+++ b/src/backend/access/hash/hashutil.c
@@ -15,7 +15,7 @@
#include "postgres.h"
#include "access/hash.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/relscan.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -286,12 +286,6 @@ _hash_checkpage(Relation rel, Buffer buf, int flags)
}
}
-bytea *
-hashoptions(Datum reloptions, bool validate)
-{
- return default_reloptions(reloptions, validate, RELOPT_KIND_HASH);
-}
-
/*
* _hash_get_indextuple_hashkey - get the hash index tuple's hash key value
*/
@@ -627,3 +621,28 @@ _hash_kill_items(IndexScanDesc scan)
else
_hash_relbuf(rel, buf);
}
+
+static options_catalog *hash_relopt_catalog = NULL;
+
+void *
+hashgetreloptcatalog(void)
+{
+ if (!hash_relopt_catalog)
+ {
+ hash_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(HashRelOptions), 1);
+
+ optionsCatalogAddItemInt(hash_relopt_catalog, "fillfactor",
+ "Packs hash index pages only to this percentage",
+ NoLock, /* No ALTER -- no lock */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER, /* fillfactor is actualy
+ * stored in metapage
+ * and should not be
+ * changed once index is
+ * created */
+ offsetof(HashRelOptions, fillfactor),
+ HASH_DEFAULT_FILLFACTOR,
+ HASH_MIN_FILLFACTOR, 100);
+ }
+ return hash_relopt_catalog;
+}
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 52dda41..9e19de2 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2732,7 +2732,10 @@ heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
+ if (IsToastRelation(relation))
+ saveFreeSpace = ToastGetTargetPageFreeSpace();
+ else
+ saveFreeSpace = HeapGetTargetPageFreeSpace(relation,
HEAP_DEFAULT_FILLFACTOR);
/* Toast and set header data in all the tuples */
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c
index 13e3bdc..8c8f023 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -323,7 +323,10 @@ RelationGetBufferForTuple(Relation relation, Size len,
len, MaxHeapTupleSize)));
/* Compute desired extra freespace due to fillfactor option */
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
+ if (IsToastRelation(relation))
+ saveFreeSpace = ToastGetTargetPageFreeSpace();
+ else
+ saveFreeSpace = HeapGetTargetPageFreeSpace(relation,
HEAP_DEFAULT_FILLFACTOR);
if (otherBuffer != InvalidBuffer)
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 7753ee7..212dcfe 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -130,7 +130,11 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
* important than sometimes getting a wrong answer in what is after all
* just a heuristic estimate.
*/
- minfree = RelationGetTargetPageFreeSpace(relation,
+
+ if (IsToastRelation(relation))
+ minfree = ToastGetTargetPageFreeSpace();
+ else
+ minfree = HeapGetTargetPageFreeSpace(relation,
HEAP_DEFAULT_FILLFACTOR);
minfree = Max(minfree, BLCKSZ / 10);
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index f93c194..71bf1a1 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -667,7 +667,10 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
len, MaxHeapTupleSize)));
/* Compute desired extra freespace due to fillfactor option */
- saveFreeSpace = RelationGetTargetPageFreeSpace(state->rs_new_rel,
+ if (IsToastRelation(state->rs_new_rel))
+ saveFreeSpace = ToastGetTargetPageFreeSpace();
+ else
+ saveFreeSpace = HeapGetTargetPageFreeSpace(state->rs_new_rel,
HEAP_DEFAULT_FILLFACTOR);
/* Now we can check to see if there's enough free space already. */
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index bf963fc..4272e60 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -1427,8 +1427,7 @@ _bt_findsplitloc(Relation rel,
state.is_rightmost = P_RIGHTMOST(opaque);
state.have_split = false;
if (state.is_leaf)
- state.fillfactor = RelationGetFillFactor(rel,
- BTREE_DEFAULT_FILLFACTOR);
+ state.fillfactor = BTGetFillFactor(rel);
else
state.fillfactor = BTREE_NONLEAF_FILLFACTOR;
state.newitemonleft = false; /* these just to keep compiler quiet */
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 3dbafdd..d164e6c 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -21,6 +21,7 @@
#include "access/nbtree.h"
#include "access/relscan.h"
#include "access/xlog.h"
+#include "access/reloptions.h"
#include "catalog/index.h"
#include "commands/vacuum.h"
#include "pgstat.h"
@@ -34,7 +35,6 @@
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
-
/* Working state for btbuild and its callback */
typedef struct
{
@@ -115,8 +115,7 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
BTCycleId cycleid);
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
-
-
+static void * btgetreloptcatalog (void);
/*
* Btree handler function: return IndexAmRoutine with access method parameters
* and callbacks.
@@ -149,7 +148,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = btvacuumcleanup;
amroutine->amcanreturn = btcanreturn;
amroutine->amcostestimate = btcostestimate;
- amroutine->amoptions = btoptions;
amroutine->amproperty = btproperty;
amroutine->amvalidate = btvalidate;
amroutine->ambeginscan = btbeginscan;
@@ -159,6 +157,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = btendscan;
amroutine->ammarkpos = btmarkpos;
amroutine->amrestrpos = btrestrpos;
+ amroutine->amrelopt_catalog = btgetreloptcatalog;
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
@@ -1390,3 +1389,26 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+
+static options_catalog *bt_relopt_catalog = NULL;
+
+static void *
+btgetreloptcatalog(void)
+{
+ if (!bt_relopt_catalog)
+ {
+ bt_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(BTRelOptions), 1);
+
+ optionsCatalogAddItemInt(bt_relopt_catalog, "fillfactor",
+ "Packs btree index pages only to this percentage",
+ ShareUpdateExclusiveLock, /* since it applies only
+ * to later inserts */
+ 0,
+ offsetof(BTRelOptions, fillfactor),
+ BTREE_DEFAULT_FILLFACTOR,
+ BTREE_MIN_FILLFACTOR, 100);
+ }
+ return bt_relopt_catalog;
+}
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index bf6c03c..5c6957a 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -344,8 +344,8 @@ _bt_pagestate(BTWriteState *wstate, uint32 level)
if (level > 0)
state->btps_full = (BLCKSZ * (100 - BTREE_NONLEAF_FILLFACTOR) / 100);
else
- state->btps_full = RelationGetTargetPageFreeSpace(wstate->index,
- BTREE_DEFAULT_FILLFACTOR);
+ state->btps_full = (BLCKSZ * (100 - BTGetFillFactor(wstate->index)) / 100);
+
/* no parent level, yet */
state->btps_next = NULL;
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index dbfb775..b01d22e 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -18,7 +18,7 @@
#include <time.h>
#include "access/nbtree.h"
-#include "access/reloptions.h"
+#include "storage/lock.h"
#include "access/relscan.h"
#include "miscadmin.h"
#include "utils/array.h"
@@ -2038,12 +2038,6 @@ BTreeShmemInit(void)
Assert(found);
}
-bytea *
-btoptions(Datum reloptions, bool validate)
-{
- return default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);
-}
-
/*
* btproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 22f64b0..77845e5 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -15,7 +15,7 @@
#include "postgres.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/spgist_private.h"
#include "access/transam.h"
#include "access/xact.h"
@@ -59,7 +59,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = spgvacuumcleanup;
amroutine->amcanreturn = spgcanreturn;
amroutine->amcostestimate = spgcostestimate;
- amroutine->amoptions = spgoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = spgvalidate;
amroutine->ambeginscan = spgbeginscan;
@@ -69,6 +68,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amendscan = spgendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = spggetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -372,8 +372,8 @@ SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
* related to the ones already on it. But fillfactor mustn't cause an
* error for requests that would otherwise be legal.
*/
- needSpace += RelationGetTargetPageFreeSpace(index,
- SPGIST_DEFAULT_FILLFACTOR);
+ needSpace += (BLCKSZ * (100 - SpGistGetFillFactor(index)) / 100);
+
needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
/* Get the cache entry for this flags setting */
@@ -537,15 +537,6 @@ SpGistInitMetapage(Page page)
}
/*
- * reloptions processing for SPGiST
- */
-bytea *
-spgoptions(Datum reloptions, bool validate)
-{
- return default_reloptions(reloptions, validate, RELOPT_KIND_SPGIST);
-}
-
-/*
* Get the space needed to store a non-null datum of the indicated type.
* Note the result is already rounded up to a MAXALIGN boundary.
* Also, we follow the SPGiST convention that pass-by-val types are
@@ -911,3 +902,27 @@ SpGistPageAddNewItem(SpGistState *state, Page page, Item item, Size size,
return offnum;
}
+
+
+
+static options_catalog *spgist_relopt_catalog = NULL;
+
+void *
+spggetreloptcatalog(void)
+{
+ if (!spgist_relopt_catalog)
+ {
+ spgist_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(SpGistRelOptions), 1);
+
+ optionsCatalogAddItemInt(spgist_relopt_catalog, "fillfactor",
+ "Packs spgist index pages only to this percentage",
+ ShareUpdateExclusiveLock, /* since it applies only
+ * to later inserts */
+ 0,
+ offsetof(SpGistRelOptions, fillfactor),
+ SPGIST_DEFAULT_FILLFACTOR,
+ SPGIST_MIN_FILLFACTOR, 100);
+ }
+ return spgist_relopt_catalog;
+}
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 6f517bb..c7ba09c 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -36,7 +36,7 @@
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_toast_pg_type_oid = InvalidOid;
-static void CheckAndCreateToastTable(Oid relOid, Datum reloptions,
+static bool CheckAndCreateToastTable(Oid relOid, Datum reloptions,
LOCKMODE lockmode, bool check);
static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
Datum reloptions, LOCKMODE lockmode, bool check);
@@ -67,23 +67,26 @@ NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
CheckAndCreateToastTable(relOid, reloptions, lockmode, false);
}
-void
+bool
NewRelationCreateToastTable(Oid relOid, Datum reloptions)
{
- CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, false);
+ return CheckAndCreateToastTable(relOid, reloptions,
+ AccessExclusiveLock, false);
}
-static void
+static bool
CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, bool check)
{
Relation rel;
+ bool success;
rel = heap_open(relOid, lockmode);
/* create_toast_table does all the work */
- (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check);
+ success = create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check);
heap_close(rel, NoLock);
+ return success;
}
/*
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 4d77411..85c517a 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -89,6 +89,8 @@ create_ctas_internal(List *attrList, IntoClause *into)
Datum toast_options;
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
ObjectAddress intoRelationAddr;
+ List *toastDefList;
+ bool toast_created;
/* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
is_matview = (into->viewQuery != NULL);
@@ -122,15 +124,20 @@ create_ctas_internal(List *attrList, IntoClause *into)
CommandCounterIncrement();
/* parse and validate reloptions for the toast table */
- toast_options = transformRelOptions((Datum) 0,
- create->options,
- "toast",
- validnsps,
- true, false);
- (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
- NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options);
+ optionsDefListValdateNamespaces(create->options, validnsps);
+ toastDefList = optionsDefListFilterNamespaces(create->options, "toast");
+
+ toast_options = transformOptions(get_toast_relopt_catalog(), (Datum) 0,
+ toastDefList, 0);
+
+ toast_created = NewRelationCreateToastTable(intoRelationAddr.objectId,
+ toast_options);
+ if (!toast_created && toast_options)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("no TOAST relation was created for a table. Can't set toast.* storage parameters")));
/* Create the "view" part of a materialized view. */
if (is_matview)
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 9ad9915..6f78d7b 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -111,7 +111,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 3f615b6..0533170 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -329,7 +329,7 @@ DefineIndex(Oid relationId,
Form_pg_am accessMethodForm;
IndexAmRoutine *amRoutine;
bool amcanorder;
- amoptions_function amoptions;
+ amrelopt_catalog_function amoption_catalog;
Datum reloptions;
int16 *coloptions;
IndexInfo *indexInfo;
@@ -535,7 +535,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amoption_catalog = amRoutine->amrelopt_catalog;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -549,10 +549,18 @@ DefineIndex(Oid relationId,
/*
* Parse AM-specific options, convert to text array form, validate.
*/
- reloptions = transformRelOptions((Datum) 0, stmt->options,
- NULL, NULL, false, false);
-
- (void) index_reloptions(amoptions, reloptions, true);
+ if (amoption_catalog)
+ {
+ reloptions = transformOptions(amoption_catalog(),
+ (Datum) 0, stmt->options, 0);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("access method %s does not support options",
+ accessMethodName)));
+ }
/*
* Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 2d4dcd7..0d27b30 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -519,7 +519,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
@@ -605,13 +604,23 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
/*
* Parse and validate reloptions, if any.
*/
- reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
- true, false);
-
if (relkind == RELKIND_VIEW)
- (void) view_reloptions(reloptions, true);
+ {
+ reloptions = transformOptions(
+ get_view_relopt_catalog(),
+ (Datum) 0, stmt->options, 0);
+ }
else
- (void) heap_reloptions(relkind, reloptions, true);
+ {
+ /* If it is not view, then it if heap */
+ char *namespaces[] = HEAP_RELOPT_NAMESPACES;
+ List *heapDefList;
+
+ optionsDefListValdateNamespaces(stmt->options, namespaces);
+ heapDefList = optionsDefListFilterNamespaces(stmt->options, NULL);
+ reloptions = transformOptions(get_heap_relopt_catalog(),
+ (Datum) 0, heapDefList, 0);
+ }
if (stmt->ofTypename)
{
@@ -3165,7 +3174,8 @@ void
AlterTableInternal(Oid relid, List *cmds, bool recurse)
{
Relation rel;
- LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
+
+ LOCKMODE lockmode = AlterTableGetLockLevel(relid, cmds);
rel = relation_open(relid, lockmode);
@@ -3207,13 +3217,15 @@ AlterTableInternal(Oid relid, List *cmds, bool recurse)
* otherwise we might end up with an inconsistent dump that can't restore.
*/
LOCKMODE
-AlterTableGetLockLevel(List *cmds)
+
+AlterTableGetLockLevel(Oid relid, List *cmds)
{
/*
* This only works if we read catalog tables using MVCC snapshots.
*/
ListCell *lcmd;
LOCKMODE lockmode = ShareUpdateExclusiveLock;
+ Relation rel;
foreach(lcmd, cmds)
{
@@ -3424,7 +3436,10 @@ AlterTableGetLockLevel(List *cmds)
* getTables() */
case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
* getTables() */
- cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
+ rel = relation_open(relid, NoLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel,NoLock);
break;
case AT_AttachPartition:
@@ -4199,7 +4214,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
errmsg("cannot rewrite system relation \"%s\"",
RelationGetRelationName(OldHeap))));
- if (RelationIsUsedAsCatalogTable(OldHeap))
+ if (HeapIsUsedAsCatalogTable(OldHeap))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot rewrite table \"%s\" used as a catalog table",
@@ -6336,11 +6351,11 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options,
/* Generate new proposed attoptions (text array) */
datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
&isnull);
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
- castNode(List, options), NULL, NULL,
- false, isReset);
- /* Validate new options */
- (void) attribute_reloptions(newOptions, true);
+
+ newOptions = transformOptions(get_attribute_options_catalog(),
+ isnull ? (Datum) 0 : datum,
+ castNode(List, options), OPTIONS_PARSE_MODE_FOR_ALTER |
+ (isReset ? OPTIONS_PARSE_MODE_FOR_RESET : 0));
/* Build new tuple. */
memset(repl_null, false, sizeof(repl_null));
@@ -10368,7 +10383,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
Datum repl_val[Natts_pg_class];
bool repl_null[Natts_pg_class];
bool repl_repl[Natts_pg_class];
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+ options_parse_mode parse_mode;
if (defList == NIL && operation != AT_ReplaceRelOptions)
return; /* nothing to do */
@@ -10397,25 +10412,52 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
&isnull);
}
- /* Generate new proposed reloptions (text array) */
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
- defList, NULL, validnsps, false,
- operation == AT_ResetRelOptions);
-
/* Validate */
+ parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;
+ if (operation == AT_ResetRelOptions)
+ parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;
+
switch (rel->rd_rel->relkind)
{
case RELKIND_RELATION:
case RELKIND_TOASTVALUE:
case RELKIND_MATVIEW:
case RELKIND_PARTITIONED_TABLE:
- (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
+ {
+ char *namespaces[] = HEAP_RELOPT_NAMESPACES;
+ List *heapDefList;
+
+ optionsDefListValdateNamespaces(defList, namespaces);
+ heapDefList = optionsDefListFilterNamespaces(
+ defList, NULL);
+ newOptions = transformOptions(get_heap_relopt_catalog(),
+ isnull ? (Datum) 0 : datum,
+ heapDefList, parse_mode);
+ }
break;
case RELKIND_VIEW:
- (void) view_reloptions(newOptions, true);
+ {
+ newOptions = transformOptions(
+ get_view_relopt_catalog(),
+ isnull ? (Datum) 0 : datum,
+ defList, parse_mode);
+ }
break;
case RELKIND_INDEX:
- (void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+ if (rel->rd_amroutine->amrelopt_catalog)
+ {
+ newOptions = transformOptions(
+ rel->rd_amroutine->amrelopt_catalog(),
+ isnull ? (Datum) 0 : datum,
+ defList, parse_mode);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("index %s does not support options",
+ RelationGetRelationName(rel))));
+ }
break;
default:
ereport(ERROR,
@@ -10429,7 +10471,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
if (rel->rd_rel->relkind == RELKIND_VIEW)
{
Query *view_query = get_view_query(rel);
- List *view_options = untransformRelOptions(newOptions);
+ List *view_options = optionsTextArrayToDefList(newOptions);
ListCell *cell;
bool check_option = false;
@@ -10489,6 +10531,8 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
{
Relation toastrel;
Oid toastid = rel->rd_rel->reltoastrelid;
+ List *toastDefList;
+ options_parse_mode parse_mode;
toastrel = heap_open(toastid, lockmode);
@@ -10512,12 +10556,15 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
&isnull);
}
+ parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;
+ if (operation == AT_ResetRelOptions)
+ parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
- defList, "toast", validnsps, false,
- operation == AT_ResetRelOptions);
+ toastDefList = optionsDefListFilterNamespaces(defList, "toast");
- (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
+ newOptions = transformOptions(get_toast_relopt_catalog(),
+ isnull ? (Datum) 0 : datum,
+ toastDefList, parse_mode);
memset(repl_val, 0, sizeof(repl_val));
memset(repl_null, false, sizeof(repl_null));
@@ -10545,6 +10592,18 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
heap_close(toastrel, NoLock);
}
+ else
+ {
+ List *toastDefList;
+
+ toastDefList = optionsDefListFilterNamespaces(defList, "toast");
+ if (toastDefList)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("TOAST relation for this table does not exist. Can't change toast.* storage parameters")));
+ }
+ }
heap_close(pgclass, RowExclusiveLock);
}
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 8559c3b..3498fef 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -332,10 +332,11 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
nulls[Anum_pg_tablespace_spcacl - 1] = true;
/* Generate new proposed spcoptions (text array) */
- newOptions = transformRelOptions((Datum) 0,
- stmt->options,
- NULL, NULL, false, false);
- (void) tablespace_reloptions(newOptions, true);
+
+ newOptions = transformOptions(
+ get_tablespace_options_catalog(),
+ (Datum) 0, stmt->options, 0);
+
if (newOptions != (Datum) 0)
values[Anum_pg_tablespace_spcoptions - 1] = newOptions;
else
@@ -1023,10 +1024,13 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
/* Generate new proposed spcoptions (text array) */
datum = heap_getattr(tup, Anum_pg_tablespace_spcoptions,
RelationGetDescr(rel), &isnull);
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
- stmt->options, NULL, NULL, false,
- stmt->isReset);
- (void) tablespace_reloptions(newOptions, true);
+
+
+ newOptions = transformOptions(get_tablespace_options_catalog(),
+ isnull ? (Datum) 0 : datum,
+ stmt->options,
+ OPTIONS_PARSE_MODE_FOR_ALTER |
+ (stmt->isReset ? OPTIONS_PARSE_MODE_FOR_RESET : 0));
/* Build new tuple. */
memset(repl_null, false, sizeof(repl_null));
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index 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.c
index 9d35a41..871dccc 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -145,7 +145,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
&rel->pages, &rel->tuples, &rel->allvisfrac);
/* Retrieve the parallel_workers reloption, or -1 if not set. */
- rel->rel_parallel_workers = RelationGetParallelWorkers(relation, -1);
+ rel->rel_parallel_workers = HeapGetParallelWorkers(relation, -1);
/*
* Make list of indexes. Ignore indexes on system catalogs if told to.
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index af99e65..5150846 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -3128,7 +3128,7 @@ transformOnConflictArbiter(ParseState *pstate,
exprLocation((Node *) onConflictClause))));
/* Same applies to table used by logical decoding as catalog table */
- if (RelationIsUsedAsCatalogTable(pstate->p_target_relation))
+ if (HeapIsUsedAsCatalogTable(pstate->p_target_relation))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("ON CONFLICT is not supported on table \"%s\" used as a catalog table",
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 27e568f..5fa2696 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1524,7 +1524,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index c04c0b5..e805bef 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2704,6 +2704,7 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
{
bytea *relopts;
AutoVacOpts *av;
+ AutoVacOpts *src;
Assert(((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_RELATION ||
((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW ||
@@ -2713,8 +2714,13 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
if (relopts == NULL)
return NULL;
+ if (((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE)
+ src = &(((ToastOptions *) relopts)->autovacuum);
+ else
+ src = &(((HeapOptions *) relopts)->autovacuum);
+
av = palloc(sizeof(AutoVacOpts));
- memcpy(av, &(((StdRdOptions *) relopts)->autovacuum), sizeof(AutoVacOpts));
+ memcpy(av, src, sizeof(AutoVacOpts));
pfree(relopts);
return av;
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 7a61af7..5ac9087 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1562,7 +1562,7 @@ ApplyRetrieveRule(Query *parsetree,
rte->rtekind = RTE_SUBQUERY;
rte->relid = InvalidOid;
- rte->security_barrier = RelationIsSecurityView(relation);
+ rte->security_barrier = ViewIsSecurityView(relation);
rte->subquery = rule_action;
rte->inh = false; /* must not be set for a subquery */
@@ -3066,7 +3066,7 @@ rewriteTargetView(Query *parsetree, Relation view)
ChangeVarNodes(viewqual, base_rt_index, new_rt_index, 0);
- if (RelationIsSecurityView(view))
+ if (ViewIsSecurityView(view))
{
/*
* The view's quals go in front of existing barrier quals: those
@@ -3102,8 +3102,9 @@ rewriteTargetView(Query *parsetree, Relation view)
*/
if (parsetree->commandType != CMD_DELETE)
{
- bool has_wco = RelationHasCheckOption(view);
- bool cascaded = RelationHasCascadedCheckOption(view);
+
+ bool has_wco = ViewHasCheckOption(view);
+ bool cascaded = ViewHasCascadedCheckOption(view);
/*
* If the parent view has a cascaded check option, treat this view as
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 82a707a..9f1ca86 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1003,6 +1003,8 @@ ProcessUtilitySlow(ParseState *pstate,
{
Datum toast_options;
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+ List *toastDefList;
+ bool success;
/* Create the table itself */
address = DefineRelation((CreateStmt *) stmt,
@@ -1023,18 +1025,24 @@ ProcessUtilitySlow(ParseState *pstate,
* parse and validate reloptions for the toast
* table
*/
- toast_options = transformRelOptions((Datum) 0,
- ((CreateStmt *) stmt)->options,
- "toast",
- validnsps,
- true,
- false);
- (void) heap_reloptions(RELKIND_TOASTVALUE,
- toast_options,
- true);
- NewRelationCreateToastTable(address.objectId,
- toast_options);
+ optionsDefListValdateNamespaces(
+ ((CreateStmt *) stmt)->options,
+ validnsps);
+
+ toastDefList = optionsDefListFilterNamespaces(
+ ((CreateStmt *) stmt)->options, "toast");
+
+ toast_options = transformOptions(
+ get_toast_relopt_catalog(), (Datum) 0,
+ toastDefList, 0);
+
+ success = NewRelationCreateToastTable(
+ address.objectId, toast_options);
+ if (!success && toast_options)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("no TOAST relation was created for a table. Can't set toast.* storage parameters")));
}
else if (IsA(stmt, CreateForeignTableStmt))
{
@@ -1101,8 +1109,13 @@ ProcessUtilitySlow(ParseState *pstate,
* lock on (for example) a relation on which we have no
* permissions.
*/
- lockmode = AlterTableGetLockLevel(atstmt->cmds);
- relid = AlterTableLookupRelation(atstmt, lockmode);
+
+ relid = AlterTableLookupRelation(atstmt, NoLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index da8b42d..e8c4309 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -149,7 +149,10 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts;
+
+ bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_catalog(), datum);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index b8e3780..cc43f09 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -460,7 +460,8 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
options = extractRelOptions(tuple,
GetPgClassDescriptor(),
relation->rd_rel->relkind == RELKIND_INDEX ?
- relation->rd_amroutine->amoptions : NULL);
+ relation->rd_amroutine->amrelopt_catalog : NULL
+ );
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index 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.h
index 0db4fc7..b226dc7 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -98,10 +98,6 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
double *indexCorrelation,
double *indexPages);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
-
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -139,6 +135,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* get catalog of reloptions definitions */
+typedef void *(*amrelopt_catalog_function) ();
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -202,7 +201,6 @@ typedef struct IndexAmRoutine
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
- amoptions_function amoptions;
amproperty_function amproperty; /* can be NULL */
amvalidate_function amvalidate;
ambeginscan_function ambeginscan;
@@ -212,6 +210,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amrelopt_catalog_function amrelopt_catalog; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 61a3880..d65ad4e 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -37,6 +37,9 @@ typedef struct BrinStatsData
#define BRIN_DEFAULT_PAGES_PER_RANGE 128
+#define BRIN_MIN_PAGES_PER_RANGE 1
+#define BRIN_MAX_PAGES_PER_RANGE 131072
+
#define BrinGetPagesPerRange(relation) \
((relation)->rd_options ? \
((BrinOptions *) (relation)->rd_options)->pagesPerRange : \
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index 3ed6743..00eccac 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -102,7 +102,6 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *brinoptions(Datum reloptions, bool validate);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index adfdb0c..451b372 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -84,7 +84,7 @@ typedef struct GinState
/* ginutil.c */
-extern bytea *ginoptions(Datum reloptions, bool validate);
+extern Datum ginhandler(PG_FUNCTION_ARGS);
extern void initGinState(GinState *state, Relation index);
extern Buffer GinNewBuffer(Relation index);
extern void GinInitBuffer(Buffer b, uint32 f);
@@ -103,6 +103,7 @@ extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
+extern void *gingetreloptcatalog(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index bfef2df..6a9090e 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -367,6 +367,24 @@ typedef struct GISTBuildBuffers
int rootlevel;
} GISTBuildBuffers;
+
+/*
+ * Definition of items of enum type. Names and codes. To add or modify item
+ * edit both lists
+ */
+#define GIST_OPTION_BUFFERING_VALUE_NAMES { \
+ "on", \
+ "off", \
+ "auto", \
+ (const char *) NULL \
+}
+typedef enum gist_option_buffering_value_numbers
+{
+ GIST_OPTION_BUFFERING_ON = 0,
+ GIST_OPTION_BUFFERING_OFF = 1,
+ GIST_OPTION_BUFFERING_AUTO = 2,
+} gist_option_buffering_value_numbers;
+
/*
* Storage type for GiST's reloptions
*/
@@ -374,7 +392,7 @@ typedef struct GiSTOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
int fillfactor; /* page fill factor in percent (0..100) */
- int bufferingModeOffset; /* use buffering build? */
+ int buffering_mode; /* use buffering build? */
} GiSTOptions;
/* gist.c */
@@ -435,7 +453,6 @@ extern bool gistvalidate(Oid opclassoid);
#define GIST_MIN_FILLFACTOR 10
#define GIST_DEFAULT_FILLFACTOR 90
-extern bytea *gistoptions(Datum reloptions, bool validate);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
@@ -485,6 +502,7 @@ extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
Datum *dst, bool *dstisnull);
extern XLogRecPtr gistGetFakeLSN(Relation rel);
+extern void *gistgetreloptcatalog(void);
/* gistvacuum.c */
extern IndexBulkDeleteResult *gistbulkdelete(IndexVacuumInfo *info,
@@ -503,7 +521,6 @@ extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup,
/* gistbuild.c */
extern IndexBuildResult *gistbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
-extern void gistValidateBufferingOption(char *value);
/* gistbuildbuffers.c */
extern GISTBuildBuffers *gistInitBuildBuffers(int pagesPerBuffer, int levelStep,
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index e3135c1..84f527b 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -271,6 +271,19 @@ typedef struct HashMetaPageData
typedef HashMetaPageData *HashMetaPage;
+
+typedef struct HashRelOptions
+{
+ int32 varlena_header_; /* varlena header (do not touch directly!) */
+ int fillfactor; /* page fill factor in percent (0..100) */
+} HashRelOptions;
+
+
+#define HashGetFillFactor(relation) \
+ ((relation)->rd_options ? \
+ ((HashRelOptions *) (relation)->rd_options)->fillfactor : \
+ HASH_DEFAULT_FILLFACTOR)
+
/*
* Maximum size of a hash index item (it's okay to have only one per page)
*/
@@ -374,7 +387,6 @@ extern IndexBulkDeleteResult *hashbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *hashvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *hashoptions(Datum reloptions, bool validate);
extern bool hashvalidate(Oid opclassoid);
extern Datum hash_any(register const unsigned char *k, register int keylen);
@@ -479,4 +491,6 @@ extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
bool bucket_has_garbage,
IndexBulkDeleteCallback callback, void *callback_state);
+extern void *hashgetreloptcatalog(void);
+
#endif /* HASH_H */
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 2d4c36d..1479646 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -427,6 +427,17 @@ typedef BTScanOpaqueData *BTScanOpaque;
#define SK_BT_DESC (INDOPTION_DESC << SK_BT_INDOPTION_SHIFT)
#define SK_BT_NULLS_FIRST (INDOPTION_NULLS_FIRST << SK_BT_INDOPTION_SHIFT)
+typedef struct BTRelOptions
+{
+ int32 varlena_header_; /* varlena header (do not touch directly!) */
+ int fillfactor; /* page fill factor in percent (0..100) */
+} BTRelOptions;
+
+#define BTGetFillFactor(relation) \
+ ((relation)->rd_options ? \
+ ((BTRelOptions *) (relation)->rd_options)->fillfactor : \
+ BTREE_DEFAULT_FILLFACTOR)
+
/*
* external entry points for btree, in nbtree.c
*/
@@ -534,7 +545,6 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.h
new file mode 100644
index 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.h
index 5cdaa3b..396263f 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -1,13 +1,9 @@
/*-------------------------------------------------------------------------
*
* reloptions.h
- * Core support for relation and tablespace options (pg_class.reloptions
+ * Support for relation and tablespace options (pg_class.reloptions
* and pg_tablespace.spcoptions)
*
- * Note: the functions dealing with text-array reloptions values declare
- * them as Datum, not ArrayType *, to avoid needing to include array.h
- * into a lot of low-level code.
- *
*
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -20,264 +16,27 @@
#define RELOPTIONS_H
#include "access/amapi.h"
-#include "access/htup.h"
-#include "access/tupdesc.h"
-#include "nodes/pg_list.h"
-#include "storage/lock.h"
-
-/* types supported by reloptions */
-typedef enum relopt_type
-{
- RELOPT_TYPE_BOOL,
- RELOPT_TYPE_INT,
- RELOPT_TYPE_REAL,
- RELOPT_TYPE_STRING
-} relopt_type;
+#include "access/options.h"
-/* kinds supported by reloptions */
-typedef enum relopt_kind
-{
- RELOPT_KIND_HEAP = (1 << 0),
- RELOPT_KIND_TOAST = (1 << 1),
- RELOPT_KIND_BTREE = (1 << 2),
- RELOPT_KIND_HASH = (1 << 3),
- RELOPT_KIND_GIN = (1 << 4),
- RELOPT_KIND_GIST = (1 << 5),
- RELOPT_KIND_ATTRIBUTE = (1 << 6),
- RELOPT_KIND_TABLESPACE = (1 << 7),
- RELOPT_KIND_SPGIST = (1 << 8),
- RELOPT_KIND_VIEW = (1 << 9),
- RELOPT_KIND_BRIN = (1 << 10),
- RELOPT_KIND_PARTITIONED = (1 << 11),
- /* if you add a new kind, make sure you update "last_default" too */
- RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_PARTITIONED,
- /* some compilers treat enums as signed ints, so we can't use 1 << 31 */
- RELOPT_KIND_MAX = (1 << 30)
-} relopt_kind;
/* reloption namespaces allowed for heaps -- currently only TOAST */
#define HEAP_RELOPT_NAMESPACES { "toast", NULL }
-/* generic struct to hold shared data */
-typedef struct relopt_gen
-{
- const char *name; /* must be first (used as list termination
- * marker) */
- const char *desc;
- bits32 kinds;
- LOCKMODE lockmode;
- int namelen;
- relopt_type type;
-} relopt_gen;
-
-/* holds a parsed value */
-typedef struct relopt_value
-{
- relopt_gen *gen;
- bool isset;
- union
- {
- bool bool_val;
- int int_val;
- double real_val;
- char *string_val; /* allocated separately */
- } values;
-} relopt_value;
-
-/* reloptions records for specific variable types */
-typedef struct relopt_bool
-{
- relopt_gen gen;
- bool default_val;
-} relopt_bool;
-
-typedef struct relopt_int
-{
- relopt_gen gen;
- int default_val;
- int min;
- int max;
-} relopt_int;
-
-typedef struct relopt_real
-{
- relopt_gen gen;
- double default_val;
- double min;
- double max;
-} relopt_real;
-
-/* validation routines for strings */
-typedef void (*validate_string_relopt) (char *value);
-
-typedef struct relopt_string
-{
- relopt_gen gen;
- int default_len;
- bool default_isnull;
- validate_string_relopt validate_cb;
- char *default_val;
-} relopt_string;
-
-/* This is the table datatype for fillRelOptions */
-typedef struct
-{
- const char *optname; /* option's name */
- relopt_type opttype; /* option's datatype */
- int offset; /* offset of field in result struct */
-} relopt_parse_elt;
-
-
-/*
- * These macros exist for the convenience of amoptions writers (but consider
- * using fillRelOptions, which is a lot simpler). Beware of multiple
- * evaluation of arguments!
- *
- * The last argument in the HANDLE_*_RELOPTION macros allows the caller to
- * determine whether the option was set (true), or its value acquired from
- * defaults (false); it can be passed as (char *) NULL if the caller does not
- * need this information.
- *
- * optname is the option name (a string), var is the variable
- * on which the value should be stored (e.g. StdRdOptions->fillfactor), and
- * option is a relopt_value pointer.
- *
- * The normal way to use this is to loop on the relopt_value array returned by
- * parseRelOptions:
- * for (i = 0; options[i].gen->name; i++)
- * {
- * if (HAVE_RELOPTION("fillfactor", options[i])
- * {
- * HANDLE_INT_RELOPTION("fillfactor", rdopts->fillfactor, options[i], &isset);
- * continue;
- * }
- * if (HAVE_RELOPTION("default_row_acl", options[i])
- * {
- * ...
- * }
- * ...
- * if (validate)
- * ereport(ERROR,
- * (errmsg("unknown option")));
- * }
- *
- * Note that this is more or less the same that fillRelOptions does, so only
- * use this if you need to do something non-standard within some option's
- * code block.
- */
-#define HAVE_RELOPTION(optname, option) \
- (pg_strncasecmp(option.gen->name, optname, option.gen->namelen + 1) == 0)
-
-#define HANDLE_INT_RELOPTION(optname, var, option, wasset) \
- do { \
- if (option.isset) \
- var = option.values.int_val; \
- else \
- var = ((relopt_int *) option.gen)->default_val; \
- (wasset) != NULL ? *(wasset) = option.isset : (dummyret)NULL; \
- } while (0)
-
-#define HANDLE_BOOL_RELOPTION(optname, var, option, wasset) \
- do { \
- if (option.isset) \
- var = option.values.bool_val; \
- else \
- var = ((relopt_bool *) option.gen)->default_val; \
- (wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \
- } while (0)
-#define HANDLE_REAL_RELOPTION(optname, var, option, wasset) \
- do { \
- if (option.isset) \
- var = option.values.real_val; \
- else \
- var = ((relopt_real *) option.gen)->default_val; \
- (wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \
- } while (0)
-
-/*
- * Note that this assumes that the variable is already allocated at the tail of
- * reloptions structure (StdRdOptions or equivalent).
- *
- * "base" is a pointer to the reloptions structure, and "offset" is an integer
- * variable that must be initialized to sizeof(reloptions structure). This
- * struct must have been allocated with enough space to hold any string option
- * present, including terminating \0 for every option. SET_VARSIZE() must be
- * called on the struct with this offset as the second argument, after all the
- * string options have been processed.
- */
-#define HANDLE_STRING_RELOPTION(optname, var, option, base, offset, wasset) \
- do { \
- relopt_string *optstring = (relopt_string *) option.gen;\
- char *string_val; \
- if (option.isset) \
- string_val = option.values.string_val; \
- else if (!optstring->default_isnull) \
- string_val = optstring->default_val; \
- else \
- string_val = NULL; \
- (wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \
- if (string_val == NULL) \
- var = 0; \
- else \
- { \
- strcpy(((char *)(base)) + (offset), string_val); \
- var = (offset); \
- (offset) += strlen(string_val) + 1; \
- } \
- } while (0)
-
-/*
- * For use during amoptions: get the strlen of a string option
- * (either default or the user defined value)
- */
-#define GET_STRING_RELOPTION_LEN(option) \
- ((option).isset ? strlen((option).values.string_val) : \
- ((relopt_string *) (option).gen)->default_len)
+extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
+ amrelopt_catalog_function catalog_fun);
/*
- * For use by code reading options already parsed: get a pointer to the string
- * value itself. "optstruct" is the StdRdOption struct or equivalent, "member"
- * is the struct member corresponding to the string option
+ * Functions that creates option catalog instances for heap, toast, view
+ * and other object types that are part of the postgres core
*/
-#define GET_STRING_RELOPTION(optstruct, member) \
- ((optstruct)->member == 0 ? NULL : \
- (char *)(optstruct) + (optstruct)->member)
+extern options_catalog *get_heap_relopt_catalog(void);
+extern options_catalog *get_toast_relopt_catalog(void);
-extern relopt_kind add_reloption_kind(void);
-extern void add_bool_reloption(bits32 kinds, char *name, char *desc,
- bool default_val);
-extern void add_int_reloption(bits32 kinds, char *name, char *desc,
- int default_val, int min_val, int max_val);
-extern void add_real_reloption(bits32 kinds, char *name, char *desc,
- double default_val, double min_val, double max_val);
-extern void add_string_reloption(bits32 kinds, char *name, char *desc,
- char *default_val, validate_string_relopt validator);
-
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- char *namspace, char *validnsps[],
- bool ignoreOids, bool isReset);
-extern List *untransformRelOptions(Datum options);
-extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern relopt_value *parseRelOptions(Datum options, bool validate,
- relopt_kind kind, int *numrelopts);
-extern void *allocateReloptStruct(Size base, relopt_value *options,
- int numoptions);
-extern void fillRelOptions(void *rdopts, Size basesize,
- relopt_value *options, int numoptions,
- bool validate,
- const relopt_parse_elt *elems, int nelems);
-
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+extern options_catalog *get_view_relopt_catalog(void);
+extern options_catalog *get_attribute_options_catalog(void);
+extern options_catalog *get_tablespace_options_catalog(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index d1bc396..780e0d7 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -20,10 +20,6 @@
#include "lib/stringinfo.h"
-/* reloption parameters */
-#define SPGIST_MIN_FILLFACTOR 10
-#define SPGIST_DEFAULT_FILLFACTOR 80
-
/* SPGiST opclass support function numbers */
#define SPGIST_CONFIG_PROC 1
#define SPGIST_CHOOSE_PROC 2
@@ -181,9 +177,6 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
-/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
-
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index 1c4b321..f8c562a 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -21,6 +21,19 @@
#include "utils/relcache.h"
+typedef struct SpGistRelOptions
+{
+ int32 varlena_header_; /* varlena header (do not touch directly!) */
+ int fillfactor; /* page fill factor in percent (0..100) */
+} SpGistRelOptions;
+
+
+#define SpGistGetFillFactor(relation) \
+ ((relation)->rd_options ? \
+ ((SpGistRelOptions *) (relation)->rd_options)->fillfactor : \
+ SPGIST_DEFAULT_FILLFACTOR)
+
+
/* Page numbers of fixed-location pages */
#define SPGIST_METAPAGE_BLKNO (0) /* metapage */
#define SPGIST_ROOT_BLKNO (1) /* root for normal entries */
@@ -380,6 +393,11 @@ typedef SpGistDeadTupleData *SpGistDeadTuple;
#define GBUF_REQ_NULLS(flags) ((flags) & GBUF_NULLS)
/* spgutils.c */
+
+/* reloption parameters */
+#define SPGIST_MIN_FILLFACTOR 10
+#define SPGIST_DEFAULT_FILLFACTOR 80
+
extern SpGistCache *spgGetCache(Relation index);
extern void initSpGistState(SpGistState *state, Relation index);
extern Buffer SpGistNewBuffer(Relation index);
@@ -407,6 +425,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page,
Item item, Size size,
OffsetNumber *startOffset,
bool errorOK);
+extern void *spggetreloptcatalog(void);
/* spgdoinsert.c */
extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h
index f82a799..c649173 100644
--- a/src/include/catalog/toasting.h
+++ b/src/include/catalog/toasting.h
@@ -19,7 +19,7 @@
/*
* toasting.c prototypes
*/
-extern void NewRelationCreateToastTable(Oid relOid, Datum reloptions);
+extern bool NewRelationCreateToastTable(Oid relOid, Datum reloptions);
extern void NewHeapCreateToastTable(Oid relOid, Datum reloptions,
LOCKMODE lockmode);
extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index da3ff5d..dc704b1 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -32,7 +32,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 4bc61e5..e276d8f 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -273,114 +273,123 @@ typedef struct AutoVacOpts
float8 analyze_scale_factor;
} AutoVacOpts;
-typedef struct StdRdOptions
+typedef struct HeapOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
int fillfactor; /* page fill factor in percent (0..100) */
AutoVacOpts autovacuum; /* autovacuum-related options */
bool user_catalog_table; /* use as an additional catalog relation */
int parallel_workers; /* max number of parallel workers */
-} StdRdOptions;
+} HeapOptions;
+
+typedef struct ToastOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ AutoVacOpts autovacuum; /* autovacuum-related options */
+} ToastOptions;
#define HEAP_MIN_FILLFACTOR 10
#define HEAP_DEFAULT_FILLFACTOR 100
+#define TOAST_DEFAULT_FILLFACTOR 100 /* Only default is actually used */
+
/*
- * RelationGetFillFactor
+ * HeapGetFillFactor
* Returns the relation's fillfactor. Note multiple eval of argument!
*/
-#define RelationGetFillFactor(relation, defaultff) \
+#define HeapGetFillFactor(relation, defaultff) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff))
+ ((HeapOptions *) (relation)->rd_options)->fillfactor : (defaultff))
/*
- * RelationGetTargetPageUsage
- * Returns the relation's desired space usage per page in bytes.
- */
-#define RelationGetTargetPageUsage(relation, defaultff) \
- (BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100)
-
-/*
- * RelationGetTargetPageFreeSpace
+ * HeapGetTargetPageFreeSpace
* Returns the relation's desired freespace per page in bytes.
*/
-#define RelationGetTargetPageFreeSpace(relation, defaultff) \
- (BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
+#define HeapGetTargetPageFreeSpace(relation, defaultff) \
+ (BLCKSZ * (100 - HeapGetFillFactor(relation, defaultff)) / 100)
/*
- * RelationIsUsedAsCatalogTable
+ * HeapIsUsedAsCatalogTable
* Returns whether the relation should be treated as a catalog table
* from the pov of logical decoding. Note multiple eval of argument!
*/
-#define RelationIsUsedAsCatalogTable(relation) \
+#define HeapIsUsedAsCatalogTable(relation) \
((relation)->rd_options && \
((relation)->rd_rel->relkind == RELKIND_RELATION || \
(relation)->rd_rel->relkind == RELKIND_MATVIEW) ? \
- ((StdRdOptions *) (relation)->rd_options)->user_catalog_table : false)
+ ((HeapOptions *) (relation)->rd_options)->user_catalog_table : false)
/*
- * RelationGetParallelWorkers
+ * HeapGetParallelWorkers
* Returns the relation's parallel_workers reloption setting.
* Note multiple eval of argument!
*/
-#define RelationGetParallelWorkers(relation, defaultpw) \
+#define HeapGetParallelWorkers(relation, defaultpw) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+ ((HeapOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+#define ToastGetTargetPageFreeSpace() \
+ (BLCKSZ * (100 - TOAST_DEFAULT_FILLFACTOR) / 100)
/*
* ViewOptions
* Contents of rd_options for views
*/
+
+/*
+ * Definition of items of enum type. Names and codes. To add or modify item
+ * edit both lists
+ */
+#define VIEW_OPTION_CHECK_OPTION_VALUE_NAMES { \
+ "local", \
+ "cascaded", \
+ (const char *) NULL \
+}
+
+typedef enum view_option_check_option_value_numbers
+{
+ VIEW_OPTION_CHECK_OPTION_NOT_SET = -1,
+ VIEW_OPTION_CHECK_OPTION_LOCAL = 0,
+ VIEW_OPTION_CHECK_OPTION_CASCADED = 1,
+} view_option_check_option_value_numbers;
+
typedef struct ViewOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
bool security_barrier;
- int check_option_offset;
+ int check_option;
} ViewOptions;
/*
- * RelationIsSecurityView
- * Returns whether the relation is security view, or not. Note multiple
- * eval of argument!
+ * ViewIsSecurityView
+ * Returns whether the view is security view, or not. Note multiple eval
+ * of argument!
*/
-#define RelationIsSecurityView(relation) \
+#define ViewIsSecurityView(relation) \
((relation)->rd_options ? \
- ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
+ ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
/*
- * RelationHasCheckOption
- * Returns true if the relation is a view defined with either the local
- * or the cascaded check option. Note multiple eval of argument!
+ * ViewHasCheckOption
+ * Returns true if view is defined with either the local or the cascaded
+ * check option. Note multiple eval of argument!
*/
-#define RelationHasCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0)
-/*
- * RelationHasLocalCheckOption
- * Returns true if the relation is a view defined with the local check
- * option. Note multiple eval of argument!
- */
-#define RelationHasLocalCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \
- strcmp((char *) (relation)->rd_options + \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \
- "local") == 0 : false)
+#define ViewHasCheckOption(relation) \
+((relation)->rd_options && \
+ ((ViewOptions *) (relation)->rd_options)->check_option != \
+ VIEW_OPTION_CHECK_OPTION_NOT_SET)
/*
- * RelationHasCascadedCheckOption
- * Returns true if the relation is a view defined with the cascaded check
- * option. Note multiple eval of argument!
+ * ViewHasCascadedCheckOption
+ * Returns true if the view is defined with the cascaded check option.
+ * Note multiple eval of argument!
*/
-#define RelationHasCascadedCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \
- strcmp((char *) (relation)->rd_options + \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \
- "cascaded") == 0 : false)
+#define ViewHasCascadedCheckOption(relation) \
+((relation)->rd_options && \
+ ((ViewOptions *) (relation)->rd_options)->check_option == \
+ VIEW_OPTION_CHECK_OPTION_CASCADED)
/*
* RelationIsValid
@@ -559,7 +568,7 @@ typedef struct ViewOptions
#define RelationIsAccessibleInLogicalDecoding(relation) \
(XLogLogicalInfoActive() && \
RelationNeedsWAL(relation) && \
- (IsCatalogRelation(relation) || RelationIsUsedAsCatalogTable(relation)))
+ (IsCatalogRelation(relation) || HeapIsUsedAsCatalogTable(relation)))
/*
* RelationIsLogicallyLogged
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 8385887..4f79224 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2378,7 +2378,9 @@ ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks set (autovacuum_enabled = false);
ERROR: unrecognized parameter "autovacuum_enabled"
alter table my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
begin;
alter view my_locks set (security_barrier=off);
select * from my_locks order by 1;
diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.out
index 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 allowed
diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.out
index f5a2993..b7550b1 100644
--- a/src/test/regress/expected/gist.out
+++ b/src/test/regress/expected/gist.out
@@ -13,7 +13,7 @@ drop index gist_pointidx2, gist_pointidx3, gist_pointidx4;
-- Make sure bad values are refused
create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
ERROR: invalid value for "buffering" option
-DETAIL: Valid values are "on", "off", and "auto".
+DETAIL: Valid values are "on", "off", "auto".
create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
ERROR: value 9 out of bounds for option "fillfactor"
DETAIL: Valid values are between "10" and "100".
@@ -27,6 +27,20 @@ insert into gist_point_tbl (id, p)
select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+ERROR: invalid value for "buffering" option
+DETAIL: Valid values are "on", "off", "auto".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
@@ -34,6 +48,7 @@ delete from gist_point_tbl where id < 10000;
vacuum analyze gist_point_tbl;
-- rebuild the index with a different fillfactor
alter index gist_pointidx SET (fillfactor = 40);
+ERROR: changing parameter "fillfactor" is not allowed
reindex index gist_pointidx;
--
-- Test Index-only plans on GiST indexes
diff --git a/src/test/regress/expected/hash_index.out b/src/test/regress/expected/hash_index.out
index e23de21..cba7665 100644
--- a/src/test/regress/expected/hash_index.out
+++ b/src/test/regress/expected/hash_index.out
@@ -219,6 +219,7 @@ INSERT INTO hash_split_heap SELECT a/2 FROM generate_series(1, 25000) a;
VACUUM hash_split_heap;
-- Rebuild the index using a different fillfactor
ALTER INDEX hash_split_index SET (fillfactor = 10);
+ERROR: changing parameter "fillfactor" is not allowed
REINDEX INDEX hash_split_index;
-- Clean up.
DROP TABLE hash_split_heap;
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
index c4107d5..231c049 100644
--- a/src/test/regress/expected/reloptions.out
+++ b/src/test/regress/expected/reloptions.out
@@ -77,7 +77,7 @@ SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
ALTER TABLE reloptions_test RESET (autovacuum_enabled,
autovacuum_analyze_scale_factor);
SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND
-reloptions IS NULL;
+ reloptions IS NULL;
reloptions
------------
@@ -86,7 +86,7 @@ reloptions IS NULL;
-- RESET fails if a value is specified
ALTER TABLE reloptions_test RESET (fillfactor=12);
ERROR: RESET must not include values for parameters
--- The OIDS option is not stored
+-- The OIDS option is not stored as reloption
DROP TABLE reloptions_test;
CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
SELECT reloptions, relhasoids FROM pg_class WHERE oid = 'reloptions_test'::regclass;
@@ -123,7 +123,24 @@ SELECT reloptions FROM pg_class WHERE oid = :toast_oid;
-- Fail on non-existent options in toast namespace
CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42);
-ERROR: unrecognized parameter "not_existing_option"
+ERROR: unrecognized parameter "toast.not_existing_option"
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+ERROR: no TOAST relation was created for a table. Can't set toast.* storage parameters
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_scale_factor"
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_threshold"
-- Mix TOAST & heap
DROP TABLE reloptions_test;
CREATE TABLE reloptions_test (s VARCHAR) WITH
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 2090a41..55b216c 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -1551,7 +1551,7 @@ SELECT * FROM base_tbl;
ALTER VIEW rw_view1 SET (check_option=here); -- invalid
ERROR: invalid value for "check_option" option
-DETAIL: Valid values are "local" and "cascaded".
+DETAIL: Valid values are "local", "cascaded".
ALTER VIEW rw_view1 SET (check_option=local);
INSERT INTO rw_view2 VALUES (-20); -- should fail
ERROR: new row violates check option for view "rw_view1"
diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.sql
index 11f8fe9..3559ef6 100644
--- a/src/test/regress/sql/brin.sql
+++ b/src/test/regress/sql/brin.sql
@@ -460,3 +460,6 @@ VACUUM ANALYZE brin_test;
EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE a = 1;
-- Ensure brin index is not used when values are not correlated
EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE b = 1;
+-- Check that changing reloptions for brin index is not allowed
+ALTER INDEX brinidx SET (pages_per_range = 10);
+ALTER INDEX brinidx RESET (pages_per_range);
diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.sql
index bae722f..9e01909 100644
--- a/src/test/regress/sql/gist.sql
+++ b/src/test/regress/sql/gist.sql
@@ -28,6 +28,15 @@ select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql
index c9119fd..21a315b 100644
--- a/src/test/regress/sql/reloptions.sql
+++ b/src/test/regress/sql/reloptions.sql
@@ -47,12 +47,12 @@ SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
ALTER TABLE reloptions_test RESET (autovacuum_enabled,
autovacuum_analyze_scale_factor);
SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND
-reloptions IS NULL;
+ reloptions IS NULL;
-- RESET fails if a value is specified
ALTER TABLE reloptions_test RESET (fillfactor=12);
--- The OIDS option is not stored
+-- The OIDS option is not stored as reloption
DROP TABLE reloptions_test;
CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
SELECT reloptions, relhasoids FROM pg_class WHERE oid = 'reloptions_test'::regclass;
@@ -75,6 +75,22 @@ SELECT reloptions FROM pg_class WHERE oid = :toast_oid;
-- Fail on non-existent options in toast namespace
CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42);
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+
-- Mix TOAST & heap
DROP TABLE reloptions_test;
^ permalink raw reply [nested|flat] 5+ messages in thread
* [PATCH 2/6] s/rewinded/rewound/
@ 2020-10-25 17:32 Justin Pryzby <[email protected]>
0 siblings, 0 replies; 5+ messages in thread
From: Justin Pryzby @ 2020-10-25 17:32 UTC (permalink / raw)
---
doc/src/sgml/ref/pg_rewind.sgml | 2 +-
src/bin/pg_rewind/parsexlog.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/src/sgml/ref/pg_rewind.sgml b/doc/src/sgml/ref/pg_rewind.sgml
index 688acdcb06..43282e6016 100644
--- a/doc/src/sgml/ref/pg_rewind.sgml
+++ b/doc/src/sgml/ref/pg_rewind.sgml
@@ -74,7 +74,7 @@ PostgreSQL documentation
<application>pg_rewind</application> with the <literal>-c</literal> option to
automatically retrieve them from the WAL archive. The use of
<application>pg_rewind</application> is not limited to failover, e.g., a standby
- server can be promoted, run some write transactions, and then rewinded
+ server can be promoted, run some write transactions, and then rewound
to become a standby again.
</para>
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 2229c86f9a..a2f1ab5422 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -207,7 +207,7 @@ findLastCheckpoint(const char *datadir, XLogRecPtr forkptr, int tliIndex,
/*
* Check if it is a checkpoint record. This checkpoint record needs to
* be the latest checkpoint before WAL forked and not the checkpoint
- * where the primary has been stopped to be rewinded.
+ * where the primary has been stopped to be rewound.
*/
info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;
if (searchptr < forkptr &&
--
2.17.0
--SavPGzlo48F1Gxyz
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="0003-comparison-but-see-also-language-changed-at-35cb574a.patch"
^ permalink raw reply [nested|flat] 5+ messages in thread
* [PATCH v2 2/6] s/rewinded/rewound/
@ 2020-10-25 17:32 Justin Pryzby <[email protected]>
0 siblings, 0 replies; 5+ messages in thread
From: Justin Pryzby @ 2020-10-25 17:32 UTC (permalink / raw)
---
doc/src/sgml/ref/pg_rewind.sgml | 2 +-
src/bin/pg_rewind/parsexlog.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/src/sgml/ref/pg_rewind.sgml b/doc/src/sgml/ref/pg_rewind.sgml
index 688acdcb06..43282e6016 100644
--- a/doc/src/sgml/ref/pg_rewind.sgml
+++ b/doc/src/sgml/ref/pg_rewind.sgml
@@ -74,7 +74,7 @@ PostgreSQL documentation
<application>pg_rewind</application> with the <literal>-c</literal> option to
automatically retrieve them from the WAL archive. The use of
<application>pg_rewind</application> is not limited to failover, e.g., a standby
- server can be promoted, run some write transactions, and then rewinded
+ server can be promoted, run some write transactions, and then rewound
to become a standby again.
</para>
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 2229c86f9a..a2f1ab5422 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -207,7 +207,7 @@ findLastCheckpoint(const char *datadir, XLogRecPtr forkptr, int tliIndex,
/*
* Check if it is a checkpoint record. This checkpoint record needs to
* be the latest checkpoint before WAL forked and not the checkpoint
- * where the primary has been stopped to be rewinded.
+ * where the primary has been stopped to be rewound.
*/
info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;
if (searchptr < forkptr &&
--
2.17.0
--QRj9sO5tAVLaXnSD
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment; filename="v2-0003-others.patch"
^ permalink raw reply [nested|flat] 5+ messages in thread
* Re: Adding a pg_get_owned_sequence function?
@ 2023-10-24 16:29 Nathan Bossart <[email protected]>
0 siblings, 1 reply; 5+ messages in thread
From: Nathan Bossart @ 2023-10-24 16:29 UTC (permalink / raw)
To: Dagfinn Ilmari Mannsåker <[email protected]>; +Cc: Tom Lane <[email protected]>; Peter Eisentraut <[email protected]>; pgsql-hackers
On Tue, Sep 12, 2023 at 03:53:28PM +0100, Dagfinn Ilmari Mannsåker wrote:
> Tom Lane <[email protected]> writes:
>> It's possible that we could get away with just summarily changing
>> the argument type from text to regclass. ISTR that we did exactly
>> that with nextval() years ago, and didn't get too much push-back.
>> But we couldn't do the same for the return type. Also, this
>> approach does nothing for the concern about the name being
>> misleading.
>
> Maybe we should go all the way the other way, and call it
> pg_get_identity_sequence() and claim that "serial" is a legacy form of
> identity columns?
Hm. Could we split it into two functions, pg_get_owned_sequence() and
pg_get_identity_sequence()? I see that commit 3012061 [0] added support
for identity columns to pg_get_serial_sequence() because folks expected
that to work, so maybe that's a good reason to keep them together. If we
do elect to keep them combined, I'd be okay with renaming it to
pg_get_identity_sequence() along with your other proposed changes.
[0] https://postgr.es/m/20170912212054.25640.55202%40wrigleys.postgresql.org
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 5+ messages in thread
* Re: Adding a pg_get_owned_sequence function?
@ 2024-01-08 05:24 vignesh C <[email protected]>
parent: Nathan Bossart <[email protected]>
0 siblings, 0 replies; 5+ messages in thread
From: vignesh C @ 2024-01-08 05:24 UTC (permalink / raw)
To: Nathan Bossart <[email protected]>; +Cc: Dagfinn Ilmari Mannsåker <[email protected]>; Tom Lane <[email protected]>; Peter Eisentraut <[email protected]>; pgsql-hackers
On Tue, 24 Oct 2023 at 22:00, Nathan Bossart <[email protected]> wrote:
>
> On Tue, Sep 12, 2023 at 03:53:28PM +0100, Dagfinn Ilmari Mannsåker wrote:
> > Tom Lane <[email protected]> writes:
> >> It's possible that we could get away with just summarily changing
> >> the argument type from text to regclass. ISTR that we did exactly
> >> that with nextval() years ago, and didn't get too much push-back.
> >> But we couldn't do the same for the return type. Also, this
> >> approach does nothing for the concern about the name being
> >> misleading.
> >
> > Maybe we should go all the way the other way, and call it
> > pg_get_identity_sequence() and claim that "serial" is a legacy form of
> > identity columns?
>
> Hm. Could we split it into two functions, pg_get_owned_sequence() and
> pg_get_identity_sequence()? I see that commit 3012061 [0] added support
> for identity columns to pg_get_serial_sequence() because folks expected
> that to work, so maybe that's a good reason to keep them together. If we
> do elect to keep them combined, I'd be okay with renaming it to
> pg_get_identity_sequence() along with your other proposed changes.
I have changed the status of the patch to "Waiting on Author" as
Nathan's comments have not yet been followed up.
Regards,
Vignesh
^ permalink raw reply [nested|flat] 5+ messages in thread
end of thread, other threads:[~2024-01-08 05:24 UTC | newest]
Thread overview: 5+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2017-10-30 09:49 Re: [PATCH] Move all am-related reloption code into src/backend/access/[am-name] and get rid of relopt_kind for custom AM Nikolay Shaplov <[email protected]>
2020-10-25 17:32 [PATCH 2/6] s/rewinded/rewound/ Justin Pryzby <[email protected]>
2020-10-25 17:32 [PATCH v2 2/6] s/rewinded/rewound/ Justin Pryzby <[email protected]>
2023-10-24 16:29 Re: Adding a pg_get_owned_sequence function? Nathan Bossart <[email protected]>
2024-01-08 05:24 ` Re: Adding a pg_get_owned_sequence function? vignesh C <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox